如何在Typescript中键入带有扩展参数的组合函数?

时间:2019-04-14 15:04:49

标签: typescript typescript-typings

据我所知,扩展运算符类型是数组。在这种情况下,fn(...args)返回以下错误:

  

“无法调用类型缺少呼叫签名的表达式。类型   “从不”没有兼容的呼叫签名。”

我尝试了几种选择,但无法提出解决方案。

const callAll = (...fns: []) => (...args: []) => fns.forEach(fn => fn && fn(...args));

1 个答案:

答案 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
)