我一直在考虑使用TypeScript进行简单包装的想法。我最终得到了一些可用于以下方面的东西:
export function logFn<T extends (...args: any[]) => any>(
fn: T,
): (...args: Parameters<T>) => ReturnType<T> {
const log = (...args: Parameters<T>): ReturnType<T> => {
console.log('log')
return fn(...args)
}
return log
}
这有效并且编译器很高兴。我的问题是关于我的最初尝试,看起来更像这样
export function logFn<T extends (...args: any[]) => any>(
fn: T,
): T {
const log: T = (...args) => {
console.log('log')
return fn(...args)
}
return log
}
这在log
变量声明中给我一个错误,错误是:
Type '(...args: any[]) => any' is not assignable to type 'T'.
'(...args: any[]) => any' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint '(...args: any[]) => any'.
似乎有一些子类型关系约束,我无法完全解决(这个错误也不能给我太多帮助)。希望有人对这方面的知识有更好的掌握,可以给我一个体面的解释,这样我就可以减少对这种行为的迷惑(我确信这是正确的)。
答案 0 :(得分:2)
“问题”是这样的:T extends (...args: any[]) => any
如果您看到以下版本,它将更加可视化:
export function logFn<T extends (...args: any[]) => any>(fn: T): T {
return (a, b) => fn(a, b);
}
您将看到错误成立
类型'(a:任何,b:任何)=>任何'都不能分配给类型'T'。 '(a:any,b:any)=> any'可分配给类型'T'的约束,但是'T'可以用约束的另一子类型实例化'(... args:any [])= >任何”。
在T extends (...args: any[]) => any
范围内,以下两项均有效
logFn((a, b) => a + b)
logFn((a, b, c) => c)
但是,如果您再参考我给出的示例,其内部定义为:
return (a, b) => fn(a, b);
所以选项2会在此处引发错误,这就是为什么打字稿会警告您。
logFn<T extends (...args: any[]) => any>(fn: T): T
我们将收到类型T
并返回类型T
。 return (a, b) => fn(a, b);
是有效的返回类型,它确实扩展了(...args: any[]) => any
,但是如何确定传递到fn
(T
)的值与该签名匹配呢? (即它可能是(...args: any[]) => any
的另一个不兼容的子类型)
不确定我的解释是否足够好,但这就是我的理解
您的解决方法很好的原因是,通过添加(...args: Parameters<T>) => ReturnType<T>
告诉编译器,参数和返回类型必须与传递的函数匹配,而不是T
,后者可以是任何其他函数定义