【问题标题】: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 违背了首先拥有类型的目的,是的。

标签: typescript typescript-typings typescript-generics


【解决方案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 确实更干净,感谢您与我一起思考。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-10-27
  • 2021-11-26
  • 1970-01-01
  • 2016-03-21
  • 2021-08-13
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多