在下面简化的我的问题的示例中:
const factory = <T extends unknown[]>(
fn: (...args: T) => void
) => {
const transformedFn = (...args: T) => {
console.log('called with', ...args)
return fn(...args)
}
// init
transformedFn() // TypeScript error here
return transformedFn
}
// usage:
const fn = (num?: number, str?: string) => {
return JSON.stringify({ num, str })
}
factory(fn)()
我收到 TypeScript 错误:
类型“[]”的参数不能分配给类型“T”的参数。“[]”可以分配给类型“T”的约束,但“T”可以用约束“unknown[]”的不同子类型来实例化。
令人困惑的是,这是有效的:
const fn = <T extends unknown[]>(...args: T) => {
return args
}
fn()
我如何才能让 TypeScript 相信T
在两种情况下它是相同的?
您已经告诉 Typescript 参数函数可能需要参数,但您调用它时没有参数,这就是它抱怨的原因。如果您传入一个需要参数的函数会怎么样?那么您会得到一个运行时错误,而这正是静态分析试图防止的。
您只需明确添加空数组作为参数的类型来约束参数函数的类型:
如果我们尝试调用
factory
具有非可选参数的函数,我们会收到类型错误:但您的原始呼叫工作正常:
您可能还注意到,我
R
为参数函数的返回类型添加了另一个泛型。这是因为在您的签名中,您将返回类型声明为,void
但实际上您在示例中返回的是字符串值。如果您确实想丢弃返回值,可以将其改回void
并删除第二个泛型。操场
这有效:
这
T extends unknown
纯粹是为了防止 TypeScript Playground 尝试将其解析为 React 代码。您的原始方法不起作用的原因是因为您可以传入一个扩展的类型
unknown[]
但不允许例如空数组,如下所示:这样就会使
transformedFn
函数主体中对没有参数的调用factory
变得不正确,因为存在一种满足T extends unknown[]
和不满足的类型[]
(这是在没有参数调用时获得的类型transformedFn
)。在此之后,您还可以保留现有代码,但也可以传递调用的参数
transformedFn
- 这也可以起作用并保留您的原始类型: