我正在尝试使用反射调用函数并同时保留其类型。
似乎T extends OnlyFunctions<Foo>
工作正常,但 K 似乎并不"记得"T 仅由AnyFunctions
组成。
我收到错误:Type 'T[K]' does not satisfy the constraint '(...args: any) => any'
解决这个问题的方法是什么?
type AnyFunction = (...args: any[]) => any;
type Only<T, S> = {
[K in keyof T]: T[K] extends S ? K : never
}[keyof T];
type OnlyFunctions<T> = Pick<T, Only<T, AnyFunction>>;
interface Foo {
a: (x1: number, x2: number) => number;
b: (y: string) => string;
}
const foo: Foo = {
a: (x: number, x1: number) => { return x + x1 + 1 },
b: (y: string) => { return y + "y" },
}
const invoke = <T extends OnlyFunctions<Foo>, K extends keyof T>(
method: K,
...args: Parameters<T[K]>,
): ReturnType<T[K]> => {
return foo[method].call(foo, args)
};
invoke("a", 1, 2)
我看到你的示例代码存在一些问题;与您的问题直接相关的是T
是一个扩展OnlyFunctions<Foo>
的泛型类型,这意味着它可能具有比OnlyFunctions<Foo>
中声明的更多的属性,由于结构子类型在 TypeScript 中的工作方式(TypeScript 中的对象类型是开放的,不完全)。 如果T extends {foo: string}
那么T
可能是{foo: string, bar: number}
或{foo: string, baz: boolean}
。 所以编译器不能假设T
的已知属性只是函数,即使OnlyFunctions<Foo>
的已知属性只是函数。
我建议完全删除T
因为它在这些示例中对您没有任何好处(没有什么可以推断的),并将其替换为OnlyFunctions<Foo>
. 你可以保持K
.
在那之后,仍然有一些问题...无效的尾随逗号,使用call
而不是apply
(或者可能使用args
而不是...args
,这取决于你如何看待它),以及当你在Foo[K]
上调用apply
或call
时,编译器无助地将其类型扩展到重载函数的联合,不幸的是这是不可调用的。 我的解决方案如下所示:
const invoke = <K extends keyof OnlyFunctions<Foo>>(
method: K,
...args: Parameters<Foo[K]>
): ReturnType<Foo[K]> => {
const fooMethod: { apply(f: Foo, args: Parameters<Foo[K]>): ReturnType<Foo[K]> } =
foo[method];
return fooMethod.apply(foo, args)
};
在这里,我通过将编译器分配给一个名为fooMethod
的中间变量来引导编译器为foo[method]
更有用的类型,其apply()
方法已知属于正确的类型。 这将正确编译和运行:
console.log(invoke("a", 1, 2)) // 4
好的,希望有帮助;祝你好运!
操场链接到代码