我在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个以上的输入数组,也会有同样的问题。
有没有什么方法可以编写这种一般的参数类型,而不需要为任何数量的参数指定大小写?
1条答案
按热度按时间xt0899hw1#
你可以把
T
中的函数generic设为输出数组的元素类型(所以如果数组产生[X, Y, Z][]
,那么T
就是[X, Y, Z]
),然后给函数一个rest parameter,它是T
上Map的元组/数组类型:在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][]
。或者至少它应该是;让我们尝试一下:
看起来不错!编译器很高兴地从输入类型生成输出类型,即使输入相对较长。
Playground代码链接