TypeScript:为联合类型嵌套对象添加属性的通用解决方案
解决TypeScript联合类型动态添加属性的类型不匹配问题
核心问题是TypeScript无法自动识别联合类型里name和attrs的关联关系。你写的逻辑没问题,但返回对象时,TS会把name推断为"test1" | "test2",attrs推断成两个扩展后的对象的联合,而Test<true>是一组name和attrs一一对应的对象联合,不是两个独立字段的联合,所以类型不匹配报错。
下面给出两种无需逐个判别类型的通用方案:
方案1:重构类型+泛型生成(更易维护)
先定义不带id的基础联合类型,再通过泛型一键生成带/不带id的版本,最后用泛型让TS追踪单个联合成员:
// 定义不带id的基础类型,后续新增类型只需要加在这里 type BaseTest = | { name: "test1"; attrs: { t1: boolean } } | { name: "test2"; attrs: { t2: string } }; // 通过泛型生成带/不带id的Test类型 type Test<T extends boolean> = BaseTest extends infer U ? U extends { name: infer N; attrs: infer A } ? { name: N; attrs: A & { id: T extends true ? string : never }; } : never : never; // 泛型函数处理单个联合成员,自动匹配返回类型 const addId = <T extends Test<false>>(node: T, id: string): Test<true> => { return { ...node, attrs: { ...node.attrs, id } } as Test<true>; };
方案2:类型转换工具(兼容原类型定义)
如果不想改动原有的类型结构,可以写一个类型转换工具,把Test<false>的成员转为对应的Test<true>成员:
// 保持你原来的类型定义不变 type Test1<T extends boolean> = { name: "test1"; attrs: { t1: boolean; id: T extends true ? string : never }; }; type Test2<T extends boolean> = { name: "test2"; attrs: { t2: string; id: T extends true ? string : never }; }; type Test<T extends boolean> = Test1<T> | Test2<T>; // 类型转换:将Test<false>成员转为Test<true>对应成员 type ToTestTrue<T extends Test<false>> = T extends Test1<false> ? Test1<true> : T extends Test2<false> ? Test2<true> : never; // 泛型函数确保输入和输出类型一一对应 const addId = <T extends Test<false>>(node: T, id: string): ToTestTrue<T> => { return { name: node.name, attrs: { ...node.attrs, id } } as ToTestTrue<T>; };
这两种方案都不需要依赖any,而且不管联合类型新增多少成员,都不用修改函数逻辑,直接复用类型定义即可。
内容的提问来源于stack exchange,提问作者Quinn Straus
相关产品推荐
相关产品推荐

