TypeScript中zip与unzip数组函数的返回类型如何定义?
TypeScript 数组转置函数zip/unzip的精准类型标注实现
你现有代码的运行时逻辑没有问题,要实现你期望的类型推导效果,不需要使用any,借助TypeScript的元组类型、映射类型、条件类型就可以完成严格的类型标注,完整实现如下:
function range(size: number, startAt: number = 0): ReadonlyArray<number> { return [...Array(size).keys()].map((i) => i + startAt); } // 映射zip单行结果的元组类型,长度不足的数组位置自动补undefined类型 type ZipRow<T extends readonly (readonly unknown[])[]> = { [K in keyof T]: T[K] extends readonly (infer V)[] ? V | undefined : never }; function zip<T extends readonly (readonly unknown[])[]>(...arrays: T): ZipRow<T>[] { const maxLength = Math.max(...arrays.map(x => x.length)); return range(maxLength).map(i => range(arrays.length).map((_, k) => arrays[k][i]) ) as ZipRow<T>[]; } // 递归处理unzip的列类型聚合 type UnzipRows<T extends readonly unknown[], Acc extends unknown[][] = []> = T extends readonly [infer First extends unknown[], ...infer Rest extends unknown[][]] ? UnzipRows<Rest, { [K in keyof Acc | keyof First]: K extends keyof Acc ? K extends keyof First ? [...Acc[K], First[K]] : Acc[K] : K extends keyof First ? [First[K]] : never }> : { [K in keyof Acc]: Acc[K][number][] }; function unzip<T extends readonly unknown[]>(array: readonly T[]): UnzipRows<T> { const maxLength = Math.max(...array.map(x => x.length)); return array.reduce( (acc, val) => { val.forEach((v, i) => acc[i].push(v)); return acc; }, range(maxLength).map(() => []) ) as UnzipRows<T>; }
类型推导效果验证
- 调用
zip([1, 2, 3], ["a", "b"])时,返回值自动推导为[number | undefined, string | undefined][],和预期一致;如果输入数组长度不一致,短数组缺失的位置会自动带上undefined类型,不会出现隐式类型漏洞。 - 调用
unzip([[1, "a"], [2, "b"], [3, undefined]])时,返回值自动推导为[number[], (string | undefined)[]],符合要求。
注:你问题中写的
unzip([1, "a"], [2, "b"], [3, undefined])调用方式和你给出的unzip函数定义不匹配,你写的unzip是接收元组数组作为单个参数,不是接收多个独立参数,如果需要多参数调用的版本,把unzip的参数改成和zip一致的rest参数写法,对应调整泛型入参即可。
说明
代码中少量的as类型断言属于合理收敛:TypeScript的类型系统无法静态推导map、reduce这类高阶函数运行时生成的元组结构,这里断言的目标类型完全由输入参数的泛型推导而来,不会丢失类型安全性,比直接标注any的实现严格很多。
该类型实现支持任意长度的输入参数,不需要为不同参数个数手动写函数重载,通用性更强。
内容的提问来源于stack exchange,提问作者fff
相关产品推荐
相关产品推荐

