TypeScript:是否可以定义接受不同类型参数的可变参数函数?
TypeScript: Is it possible to define a variadic function that accepts different types of arguments?
我想编写一个通用函数,它接受可能具有不同类型的可变数量的参数和returns基于这些参数的元组。
下面是 JavaScript 中的示例:
function evaluate (...fns) {
return fns.map(fn => fn())
}
evaluate(
() => 10
) // [ 10 ]
evaluate(
() => 10,
() => 'f',
() => null
) // [ 10, 'f', null ]
在 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]
我尝试了一种天真的方法,为所有合理长度的元组创建重载:
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]
}
但它看起来很糟糕,不能很好地扩展并且会导致更复杂的函数体出现问题。
有什么方法可以动态完成吗?谢谢!
最简单的实现方法是 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:
function evaluate<T extends any[]>(
...fns: { [I in keyof T]: () => T[I] }
) {
return fns.map(fn => fn()) as T;
}
请注意 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. See 了解更多信息。
因为 {[I in keyof T]: () => T[I]}
是一个 同态 映射类型,我们直接在 keyof T
上映射(参见 for more information), the compiler is able to infer T
from it(链接页面已弃用,但仍然准确,没有新页面存在 ♂️)。
让我们看看实际效果:
const x = evaluate(() => 10);
// const x: [number]
const y = evaluate(
() => 10,
() => 'f',
() => null
)
// const y: [number, string, null]
看起来不错。编译器发现 x
是 [number]
类型,y
是 [number, string, null]
类型。在您传入未知 order/length:
的剩余参数的情况下,它的行为也很合理
const fs = [() => "a", () => 3];
// const fs: ((() => string) | (() => number))[]
const z = evaluate(...fs);
// const z: (string | number)[]
这里fs
是Array<(()=>string) | (()=>number)>
类型,所以z
是类似的类型Array<string | number>
。
我想编写一个通用函数,它接受可能具有不同类型的可变数量的参数和returns基于这些参数的元组。
下面是 JavaScript 中的示例:
function evaluate (...fns) {
return fns.map(fn => fn())
}
evaluate(
() => 10
) // [ 10 ]
evaluate(
() => 10,
() => 'f',
() => null
) // [ 10, 'f', null ]
在 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]
我尝试了一种天真的方法,为所有合理长度的元组创建重载:
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]
}
但它看起来很糟糕,不能很好地扩展并且会导致更复杂的函数体出现问题。
有什么方法可以动态完成吗?谢谢!
最简单的实现方法是 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:
function evaluate<T extends any[]>(
...fns: { [I in keyof T]: () => T[I] }
) {
return fns.map(fn => fn()) as T;
}
请注意 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. See
因为 {[I in keyof T]: () => T[I]}
是一个 同态 映射类型,我们直接在 keyof T
上映射(参见 T
from it(链接页面已弃用,但仍然准确,没有新页面存在 ♂️)。
让我们看看实际效果:
const x = evaluate(() => 10);
// const x: [number]
const y = evaluate(
() => 10,
() => 'f',
() => null
)
// const y: [number, string, null]
看起来不错。编译器发现 x
是 [number]
类型,y
是 [number, string, null]
类型。在您传入未知 order/length:
const fs = [() => "a", () => 3];
// const fs: ((() => string) | (() => number))[]
const z = evaluate(...fs);
// const z: (string | number)[]
这里fs
是Array<(()=>string) | (()=>number)>
类型,所以z
是类似的类型Array<string | number>
。