我有一个具有多个方法的对象,每个方法都有不同的参数,我正在尝试定义一个包装函数,该函数将接受来自该对象的方法并返回具有相同签名的函数。
Here是来自 TypeScript Playground 的示例。
interface myFunctions{
one: () => number;
two: () => number;
echo: (str: string) => string;
}
const myFunctions: myFunctions = {
one: () => 1,
two: () => 2,
echo: (str: string) => str
}
这是我当前的包装器定义:
const wrapper = <T extends keyof myFunctions> (
func: myFunctions[T]
) => (...args: Parameters<myFunctions[T]>) => func(...args)
这是我想如何使用包装器的示例:
wrapper<'one'>(myFunctions.one)()
wrapper<'echo'>(myFunctions.echo)('hello')
Typescript 不接受我对包装器的定义,如 Parameters<myFunctions[T]>
可以是空数组或参数str
,以及函数 echo
需要一个字符串。
我对 Typescript 不是很精通,翻阅了 Typescript 手册,但找不到答案。关于 StackOverflow 中包装器的类似问题也不是我想要的。
定义 wrapper
的正确方法是什么? ?
最佳答案
每次开始输入内容时,问问自己:“如果我是编译器,我会期望什么?”。如果您分析包装函数的问题,您会注意到您需要让编译器知道:
func
参数满足(...args: any[]) => any
约束(即“任意函数”)。args
必须推断剩余参数(您使用Parameters
实用程序类型解决)。- 还必须推断结果函数的返回类型(您错过了这个)。它可以通过
ReturnType
实用程序类型轻松实现。
您不必告诉编译器,关键是什么 - 它足够智能,可以根据使用情况推断出函数的类型。现在,你如何重写你的类型?结合步骤 1 到 3,您将得到以下结果:
const wrapper = <U extends (...args: any[]) => any>(func: U) => (...args: Parameters<U>) : ReturnType<U> => func(...args);
const one = wrapper(myFunctions.one); //() => number
const two = wrapper(myFunctions.two); //() => number
const echo = wrapper(myFunctions.echo); //(str: string) => string
生成的泛型不限于您的 myFunctions
类型:
const rand = wrapper((a:string,b:number,c:boolean) => {}); //(a: string, b: number, c: boolean) => void
如果您只需要接受 myFunctions
类型的函数,只需让编译器知道 U
必须与 myFunctions
中的签名之一完全匹配> 成员。
要实现这一点,您需要保证类型“相同”。这可以通过双重条件类型来完成。它的一般形式如下所示:
A extends B ? B extends A ? C : never : never;
让我们将这个想法应用到用例中并创建一个辅助类型:
type IsOneOf<T,F> = { [ P in keyof T ] : F extends T[P] ? T[P] extends F ? T[P] : never : never; }[keyof T];
如果 T
没有匹配的成员,则上述内容将解析为 never
,否则解析为匹配的成员。由于没有任何内容与 never
兼容,因此我们得到了所需的行为:
type IsOneOf<T,F> = { [ P in keyof T ] : F extends T[P] ? T[P] extends F ? T[P] : never : never; }[keyof T];
const wrapper = <U extends (...args: any[]) => any>(func: U extends IsOneOf<myFunctions, U> ? U : never) => (...args: Parameters<U>) : ReturnType<U> => func(...args);
const one = wrapper(myFunctions.one);
const two = wrapper(myFunctions.two);
const echo = wrapper(myFunctions.echo);
const rand = wrapper((a:string,b:number,c:boolean) => {}); //not assignable to parameter of type 'never'.
const hm = wrapper(() => 'a'); //also error
关于typescript - 如何正确键入对象方法的包装函数?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/66199621/