TypeScript中DataAddress类型额外分支的成因及修复方案
TypeScript类型分支疑问与修正
代码示例
/* eslint-disable */ type Primitive = boolean | null | number | string; export type DataAddress<T> = T extends Primitive ? [T] : ObjectAddress<T>; export type ObjectAddress<T> = { [K in keyof T]: [K, ...DataAddress<T[K]>]; }[keyof T]; type X = { x: boolean[] } function f(...args: DataAddress<X>) { } f('x1') /* TS2345: Argument of type ["x1"] is not assignable to parameter of type ["x", ...any[]] | ["x", number, false] | ["x", number, true] */
问题
类型中的["x", ...any[]]分支来自何处?如何修改代码,使该类型仅为["x", number, false] | ["x", number, true]?
一、["x", ...any[]]分支的来源
问题出在数组类型的处理逻辑上:
boolean[]不属于Primitive定义的原始类型,所以会进入ObjectAddress<T>的处理流程。- 在TypeScript中,数组本质是带
number索引签名的对象,当用[K in keyof T]遍历数组的键时,TypeScript会保留数组的宽泛索引签名兼容性——这会导致DataAddress<T[K]>被推断成...any[],最终生成["x", ...any[]]这个不符合预期的分支。
简单来说,就是数组的对象特性让泛型映射时产生了宽泛的类型分支。
二、修改方案
要精准生成目标类型,需要在DataAddress中单独处理数组类型,避免直接丢给ObjectAddress处理。修改后的代码如下:
/* eslint-disable */ type Primitive = boolean | null | number | string; // 新增数组类型的专属处理逻辑 export type DataAddress<T> = T extends Primitive ? [T] : T extends Array<infer U> ? DataAddress<U> extends [infer V] ? [number, V] : never : ObjectAddress<T>; export type ObjectAddress<T> = { [K in keyof T]: [K, ...DataAddress<T[K]>]; }[keyof T]; type X = { x: boolean[] } function f(...args: DataAddress<X>) { } // 此时DataAddress<X>的类型为["x", number, false] | ["x", number, true] f('x1')
修改说明
- 新增数组判断逻辑:通过
T extends Array<infer U>提取数组的元素类型U(此处为boolean)。 - 对数组元素的类型递归处理:因为
boolean是原始类型,DataAddress<U>会得到[false] | [true],再映射成[number, false] | [number, true]。 - 最后代入
ObjectAddress<X>后,就会生成精准的["x", number, false] | ["x", number, true],完全符合需求。
内容的提问来源于stack exchange,提问作者Grief
相关产品推荐
相关产品推荐

