TypeScript:要在返回类型中使用的rest参数的泛型类型

pinkon5k  于 2023-03-04  发布在  TypeScript
关注(0)|答案(1)|浏览(153)

我在TypeScript中有一个ZIP合成函数,它以任意数量的数组作为参数,应该返回一个新数组,每个输入数组都有一个元素。问题是返回数组的类型。
该函数的返回类型应该与python或lodash中的zip函数相同。

function zipComp<T>(...arrs: T[][]) { ... }
zipComp([1, 2], [false, true]) // expected: [number, boolean][], actual: unknown[]
zipComp(["a", "b", "c"], [1, 2, 3], [{}, {}, {}]) // expected: [string, number, object][], actual: unknown[]

我看了洛达什的类型,看看他们是怎么做到的。

// from lodash (array.d.ts)
zip<T1, T2>(arrays1: List<T1>, arrays2: List<T2>): Array<[T1, T2]>;
zip<T1, T2, T3>(arrays1: List<T1>, arrays2: List<T2>, arrays3: List<T3>): Array<[T1, T2, T3]>;
zip<T1, T2, T3, T4>(arrays1: List<T1>, arrays2: List<T2>, arrays3: List<T3>, arrays4: List<T4>): Array<[T1, T2, T3, T4]>;
zip<T1, T2, T3, T4, T5>(arrays1: List<T1>, arrays2: List<T2>, arrays3: List<T3>, arrays4: List<T4>, arrays5: List<T5>): Array<[T1, T2, T3, T4, T5]>;
zip<T>(...arrays: Array<List<T>>): Array<Array<T>>; // => here is the problem

这种实现可以工作,但是如果有5个以上的输入数组,也会有同样的问题。
有没有什么方法可以编写这种一般的参数类型,而不需要为任何数量的参数指定大小写?

xt0899hw

xt0899hw1#

你可以把T中的函数generic设为输出数组的元素类型(所以如果数组产生[X, Y, Z][],那么T就是[X, Y, Z]),然后给函数一个rest parameter,它是T上Map的元组/数组类型:

declare function zipComp<T extends any[]>(
    ...args: { [I in keyof T]: T[I][] }
): T[]

在Map类型{ [I in keyof T]: T[I][] }中,对于T数组中的每个类似数字的索引I,该索引处的元素类型T[I]被Map到这些元素T[I][]的数组。因此,如果T[X, Y, Z],则Map类型是[X[], Y[], Z[]]
注意,因为所讨论的Map类型是 * homomorphic *(如What does "homomorphic mapped type" mean?中所描述的),编译器能够从为args传入的值推断T(如果这不是真的,那么推断可能失败,我们需要重写调用签名)。
还要注意,rest参数类型倾向于被编译器推断为元组类型,而不仅仅是无序数组类型,这正是我们想要的。
所有这些都意味着,如果args的类型为[X[], Y[], Z[]],那么输出的类型将为[X, Y, Z][]
或者至少它应该是;让我们尝试一下:

const nb = zipComp([1, 2], [false, true]);
// const nb: [number, boolean][];

const sno = zipComp(["a", "b", "c"], [1, 2, 3], [{}, {}, {}]);
// const sno: [string, number, {}][];

const hmm = zipComp([""], [1], [true], [new Date()], [null], 
   [undefined], [Symbol()], [() => 2]);
// const hmm: [string, number, boolean, Date, null, 
//   undefined, symbol, () => 2][]

看起来不错!编译器很高兴地从输入类型生成输出类型,即使输入相对较长。
Playground代码链接

相关问题