类泛型类型的打字稿方法允许任何

Posted

技术标签:

【中文标题】类泛型类型的打字稿方法允许任何【英文标题】:Typescript method of class generic type allows any 【发布时间】:2021-06-09 21:40:46 【问题描述】:

几乎有一个小例外,我的约束似乎仍然允许任何例外。 谁能告诉我我在这里缺少什么。

用例:

    class A 
        public b()
        public c:number = 0;
        public d:any;
    ;

当前实现:

    function methodOfClass<
        T extends  new (...args: any[]): any;  & Function, 
        M extends (this:T, ...args:any[]) => any =  
            [K in keyof T]: T[K] extends (this:T, ...args:any[]) => any 
                ? T[K] 
                : never
        [keyof T]
    >(ctor:T, method:M, args?:Parameters<M>) : ReturnType<M> 
        return method.call(ctor, args ?? []);
    
    
    const m0 = methodOfClass(A, A.prototype.b); // correct
    const m1 = methodOfClass(A, A.prototype.c); // correct 
    const m2 = methodOfClass(A, A.prototype.d); // incorrect :-(

受此线程的启发,我还管理了以下内容,但找不到合适的使用方法? How to create a type excluding instance methods from a class in typescript?

    type Methods<T> = Omit<T,  
        [K in keyof T]: T[K] extends (this:T, ...args:any[]) => any 
            ? never 
            : K 
    [keyof T]>;
    
    type M = Methods<A>; // correct

【问题讨论】:

any 可以分配给任何东西 正如@RobertoZvjerković 所说,any 可以是任何东西。它可以是一种方法,也可以是一种属性。所以这是预期的行为。您的methodOfClass 输入没有错误。唯一的“错误”是在课堂上使用any 我完全同意使用 any 违背了首先拥有类型的目的,是的。 【参考方案1】:

any 可以是 Function

any 可以是任何东西,包括Function。您的methodOfClass 函数无法知道您打算 d 成为一个属性。 d 是一个函数就很好了。

const a = new A();
a.d = () => "hello world"; // no error because `() => string` is assignable to `any`

换句话说,您看到的m2 的结果是预期的行为,它不是“不正确的”。 any 表示d 可以 是一个函数,并且可以使用任何参数调用它。所以这也很好:

const m3 = methodOfClass(A, A.prototype.d, ["something", 999]);

显然这样做有很大的运行时错误的可能性。

这里唯一的解决方案是不要在类属性上使用any

不要让args 成为可选项

any 的问题无关,我强烈建议您更改调用方法的方式以避免运行时错误。如果函数需要参数但您只使用默认的[],那么您将遇到问题。

(ctor: T, method: M, ...args: Parameters<M>): ReturnType<M> 
    return method.call(ctor, ...args);

使用... 允许您在不需要参数时不传递任何内容,但要求您传递所有必需的参数(作为单独的参数传递给methodOfClass,而不是作为数组)。

class E 
    public e(n: number): number 
        return n + 1;
    
;

const m0 = methodOfClass(A, A.prototype.b); // this is still the same
console.log(m0); // undefined

const m3 = methodOfClass(E, E.prototype.e, 6); // but here we need a `number`
console.log(m3); // 7

const m4 = methodOfClass(E, E.prototype.e); // error because args missing

Typescript Playground Link

【讨论】:

spread/rest 确实更干净,感谢您与我一起思考。

以上是关于类泛型类型的打字稿方法允许任何的主要内容,如果未能解决你的问题,请参考以下文章

如何在打字稿的泛型类中创建类型为“T”的新对象?

自定义泛型结构:泛型类泛型接口泛型方法

打字稿泛型类参数

反射获取泛型类泛型方法

unity的C#学习——泛型的创建与继承泛型集合类泛型中的约束和反射

打字稿可变泛型类,接受扩展公共基础的可变数量的类对象?