使用条件类型从对象反射带有参数和返回类型的方法



我正在尝试使用反射调用函数并同时保留其类型。

似乎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]上调用applycall时,编译器无助地将其类型扩展到重载函数的联合,不幸的是这是不可调用的。 我的解决方案如下所示:

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

好的,希望有帮助;祝你好运!

操场链接到代码

相关内容

  • 没有找到相关文章

最新更新