简体   繁体   English

TypeScript:是否可以定义一个接受不同类型 arguments 的可变参数 function?

[英]TypeScript: Is it possible to define a variadic function that accepts different types of arguments?

I want to write a generic function that accepts variable number of arguments that may have different types and returns a tuple based on those arguments.我想写一个通用的 function,它接受可变数量的 arguments,它可能有不同的类型,并返回一个基于那些 arguments 的元组。

Here is an example in JavaScript:这是 JavaScript 中的示例:

function evaluate (...fns) {
  return fns.map(fn => fn())
}

evaluate(
  () => 10
) // [ 10 ]

evaluate(
  () => 10,
  () => 'f',
  () => null
) // [ 10, 'f', null ]

And in TypeScript I need to somehow convert the spread argument tuple to a resulting one:在 TypeScript 中,我需要以某种方式将传播参数元组转换为结果元组:

function evaluate<T1, T2 ... Tn> (
  ...fns: [() => T1, () => T2 ... () => Tn]
): [T1, T2 ... Tn] {
  return fns.map(fn => fn()) as [T1, T2 ... Tn]
}

evaluate(
  () => 10
) // [ 10 ]: [number]

evaluate(
  () => 10,
  () => 'f',
  () => null
) // [ 10, 'f', null ]: [number, string, null]

I've tried a naive approach of creating an overload for all reasonable lengths of tuple:我尝试了一种为所有合理长度的元组创建重载的天真方法:

function evaluate<T1> (
  fn1: () => T1
): [T1]
function evaluate<T1, T2> (
  fn1: () => T1,
  fn2: () => T2
): [T1, T2]
function evaluate<T1, T2, T3> (
  fn1: () => T1,
  fn2: () => T2,
  fn3: () => T3
): [T1, T2, T3]
function evaluate<T1, T2, T3> (
  ...fns: Array<(() => T1) | (() => T2) | (() => T3)>
): [T1] | [T1, T2] | [T1, T2, T3] {
  return fns.map(fn => fn()) as [T1] | [T1, T2] | [T1, T2, T3]
}

But it looks horribly, doesn't scale well and causes issues with a more complex function body.但它看起来很糟糕,不能很好地扩展并导致更复杂的 function 主体出现问题。

Is there any way this could be done dynamically?有什么办法可以动态地完成吗? Thanks!谢谢!

The easiest way to implement this is to make evaluate() generic in its arraylike output type T (intended to be a tuple type ), and then represent the fns rest parameter as a mapped type on T , noting that mapped array/tuple types are also array/tuple types :实现这一点的最简单方法是使evaluate()在其类数组 output 类型T (旨在成为元组类型)中成为通用的,然后将fns rest 参数表示为T上的映射类型,注意映射的数组/元组类型是还有数组/元组类型

function evaluate<T extends any[]>(
  ...fns: { [I in keyof T]: () => T[I] }
) {
  return fns.map(fn => fn()) as T;
}

Note that the type assertion as T is necessary because the compiler cannot see that fns.map(fn => fn()) will have the effect of converting an array/tuple of function types to the array/tuple of corresponding return types.请注意, as T类型断言是必要的,因为编译器看不到fns.map(fn => fn())具有将 function 类型的数组/元组转换为相应返回类型的数组/元组的效果。 See Mapping tuple-typed value to different tuple-typed value without casts for more information.有关详细信息,请参阅将元组类型值映射到不同的元组类型值而不进行强制转换

Because {[I in keyof T]: () => T[I]} is a homomorphic mapped type where we are mapping directly over keyof T (see What does "homomorphic mapped type" mean? for more information), the compiler is able to infer T from it (linked page is deprecated, but still accurate and no new page exists ♂️).因为{[I in keyof T]: () => T[I]}是一个同态映射类型,我们直接在keyof T上进行映射(有关更多信息,请参阅“同态映射类型”是什么意思? ),编译器是能够从中推断出T (链接页面已弃用,但仍然准确并且不存在新页面 ♂️)。

Let's see it in action:让我们看看它的实际效果:

const x = evaluate(() => 10);
// const x: [number]

const y = evaluate(
  () => 10,
  () => 'f',
  () => null
)
// const y: [number, string, null]

Looks good.看起来挺好的。 The compiler sees that x is of type [number] and y is of type [number, string, null] .编译器发现x[number]类型, y[number, string, null]类型。 It also behaves reasonably in cases where you pass in a rest argument of unknown order/length:在您传入未知顺序/长度的 rest 参数的情况下,它的行为也很合理:

const fs = [() => "a", () => 3];
// const fs: ((() => string) | (() => number))[]

const z = evaluate(...fs);
// const z: (string | number)[]

Here fs is of the type Array<(()=>string) | (()=>number)>这里fs的类型是Array<(()=>string) | (()=>number)> Array<(()=>string) | (()=>number)> , and so z is of the analogous type Array<string | number> Array<(()=>string) | (()=>number)> ,所以z是类似的类型Array<string | number> Array<string | number> . Array<string | number>

Playground link to code 游乐场代码链接

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM