如何修改TypeScript的ArrayBuilder类型以支持可选元素与展开语法?
TypeScript ArrayBuilder工具类型支持可选元素的修复方案
问题背景
现有ArrayBuilder工具类型可正确处理带展开(spread)标记的数组类型定义,例如Test1能生成预期的[string, ...string[], number]。但该类型未支持optional: true的元素,导致Test2当前生成[string, boolean, ...string[]],而预期结果应为[string, boolean?, ...string[]]。原实现中,展开语法[...A,...ItemToSpread]与可选属性的索引签名语法兼容存在问题,需针对性修复。
原代码实现
type Def = { type: any, spread: boolean, optional: boolean } type A = { type: string, spread: false, optional: false } type B = { type: boolean, spread: false, optional: true } type C = { type: string[], spread: true, optional: false } type D = { type: number, spread: false, optional: false } type ArrayBuilder< T extends Def[], A extends any[] = [], B extends any[] = [], C extends any[] = [], State extends 'A' | 'B' = 'A', > = T extends [ infer H extends Def, ...infer R extends Def[], ] ? State extends 'A' ? H['spread'] extends true ? ArrayBuilder<R, A, H['type'], [], 'B'> : ArrayBuilder<R, [...A, H['type']], [], [], 'A'> :ArrayBuilder<R, A, B, [...C, H['type']], 'B'> : [...A, ...B, ...C] type Test1 = ArrayBuilder<[A,C,D]> // [string, ...string[], number] // 需要修改上述代码,使Test2生成[string, boolean?, ...string[]] type Test2 = ArrayBuilder<[A,B,C]> // 当前为[string, boolean, ...string[]] - 需改为[string, boolean?, ...string[]]
修复方案
通过新增辅助类型处理元素的可选性,并调整ArrayBuilder中元素拼接的逻辑,实现可选元组项的支持:
type Def = { type: any, spread: boolean, optional: boolean } type A = { type: string, spread: false, optional: false } type B = { type: boolean, spread: false, optional: true } type C = { type: string[], spread: true, optional: false } type D = { type: number, spread: false, optional: false } // 辅助类型:根据optional标记将元素转为可选元组项 type Optionalize<T, IsOptional extends boolean> = IsOptional extends true ? T? : T; type ArrayBuilder< T extends Def[], A extends any[] = [], B extends any[] = [], C extends any[] = [], State extends 'A' | 'B' = 'A', > = T extends [ infer H extends Def, ...infer R extends Def[], ] ? State extends 'A' ? H['spread'] extends true ? ArrayBuilder<R, A, H['type'], [], 'B'> : ArrayBuilder<R, [...A, Optionalize<H['type'], H['optional']>], [], [], 'A'> : ArrayBuilder<R, A, B, [...C, Optionalize<H['type'], H['optional']>], 'B'> : [...A, ...B, ...C] type Test1 = ArrayBuilder<[A,C,D]> // [string, ...string[], number] - 符合预期 type Test2 = ArrayBuilder<[A,B,C]> // [string, boolean?, ...string[]] - 符合预期
修复说明
- 辅助类型
Optionalize:根据元素的optional布尔值,将元素类型转为可选(T?)或保持原样。TypeScript元组原生支持可选项语法,无需使用索引签名的方式,避免了与展开语法的兼容问题。 - 调整元素拼接逻辑:在处理非spread元素(无论处于State'A'还是'B'阶段)时,使用
Optionalize处理元素类型,确保可选标记被正确转换为元组中的可选项。 - 保留原有展开逻辑:spread元素的处理逻辑保持不变,确保
...string[]这类展开类型仍能正确生成。
内容的提问来源于stack exchange,提问作者TrevTheDev
相关产品推荐
相关产品推荐

