我想创建一个函数,它可以包装任意函数,但参数和结果都保持相同的类型。例如,这将包装一个函数-但是类型将变成any
:
// Note that the returned wrappedFn takes the same arguments as fn, and returns
// a value of the same type as the one returned by fn.
function myWrapper(fn: (...args: any[]) => any): (...args: any[]) => any {
return function wrappedFn(...args: any[]) {
// ...some arbitrary code goes here
return fn(...args);
};
}
function repeatString(str: string, times: number): string {
return Array(times + 1).join(str);
}
// TS is aware that wrappedRepeatString is a function, but it infers that
// it takes any parameters, rather than a string and a number.
const wrappedRepeatString = myWrapper(repeatString);
// The result of this call is "foofoofoo" (a string), but TS sees it as "any".
const repeatedString = wrappedRepeatString("foo", 3);
// Since repeatedString is of type "any", this compiles, even though
// it'd crash as there's no "toFixed" method on strings.
repeatedString.toFixed();
我可以输入myWrapper
来接受一个函数,它接受一个字符串和一个数字作为参数,并返回一个数字-但是如果我想将它与具有不同参数或返回类型的函数一起使用,我需要编写另一个。理想情况下,我可以编写一个包装器,TS可以推断出wrappedFn
(返回函数)的参数和返回值与fn的参数和返回值相同。
请注意,仅仅将其余参数输入为数组类型(例如...args: string[]
)是行不通的,因为参数也可能是不同的类型。
您可以使用一个泛型变量来描述函数fn
,并要求组合函数具有相同的签名。
将函数作为泛型,利用内置的实用程序类型ReturnType<T>
和Parameters<T>
:
function myWrapper<F extends (...args: any[]) => any>(fn: F) {
return function wrappedFn(...args: Parameters<F>): ReturnType<F> {
return fn(...args);
};
}
请注意,虽然组合函数具有与F
相同的参数和返回值,但它不能完全赋值给F
,因为javascript中的函数是对象,因此F
类型可能包含一些组合函数不具有的任意额外属性。
使用Args
和Return
类型作为两个独立的泛型:
function myWrapper<Args extends any[], Return>(fn: (...args: Args) => Return) {
return function wrappedFn(...args: Args): Return {
return fn(...args);
};
}
这两个都给出了repeatedString.toFixed()
所需的错误,因为repeatedString
的类型现在是string
而不是any
。