[] 实际上是一个空元组,因此该元组的一个项目是never 类型(即就打字稿而言永远不存在的东西)。如果您想要一个不想检查的项目数组,any[] 就是这样写的方式。
const callAll = (...fns: any[]) => (...args: any[]) => fns.forEach(fn => fn && fn(...args))
虽然这会通过编译器,但它的类型不是很安全,我们可以使用任何参数调用 callAll,打字稿不会抱怨(从编译器的角度来看,callAll(1,2,3) 是可以的)
第一个改进是告诉 typescript 传递给 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
)