据我所知,扩展运算符类型是数组。在这种情况下,fn(...args)
返回以下错误:
“无法调用类型缺少呼叫签名的表达式。类型 “从不”没有兼容的呼叫签名。”
我尝试了几种选择,但无法提出解决方案。
const callAll = (...fns: []) => (...args: []) => fns.forEach(fn => fn && fn(...args));
答案 0 :(得分:3)
[]
实际上是一个空的元组,因此该元组的项的类型为never
(即就打字稿而言永远不会存在的某项)。如果您想要一系列项目,则不希望选中any[]
就是这样。
const callAll = (...fns: any[]) => (...args: any[]) => fns.forEach(fn => fn && fn(...args))
虽然这将通过编译器,但它不是非常安全的类型,但是我们可以使用任何参数调用callAll
,而打字稿也不会抱怨(callAll(1,2,3)
从编译器的角度来看是可以的)
第一个改进是告诉打字稿,传递给fn
的数组必须是一个函数数组:
const callAll = (...fns: Array<(...a: any[])=> any>) => (...args: any[]) => fns.forEach(fn => fn && fn(...args));
const composed = callAll(a => console.log("a " + a), b => console.log("b " + b))
composed("arg");
我使用Array<T>
语法而不是T[]
,两者代表相同的类型,但是由于T
是函数签名((...a: any[])=> any
),因此该语法更易于阅读。函数签名将允许任何函数进入数组,而无需以任何方式对其进行核化。
尽管有所改进,但这仍然不是完美的。没有检查所有函数的参数是否匹配,也没有与传入的参数匹配。
我们可以做得更好,检查参数类型是否匹配并且参数类型也匹配。为此,我们将需要在函数中添加泛型类型参数。 P
将代表参数的类型。这将使我们将参数类型转发给返回的函数,并强制所有函数必须具有相同的参数类型:
const callAll = <P extends any[]>(...fns: Array<(...a: P)=> void>) => (...args: P) => fns.forEach(fn => fn && fn(...args));
const composed = callAll(
(a: string) => console.log("a " + a), // only first one must specify param types
b => console.log("b " + b)
) // b is inferred as string
composed("arg");
composed(1); //error must be strings
const composedBad = callAll(
(a: string) => console.log("a " + a),
(b: number) => console.log("b " + b) // error parametr types don't match
)