如何深度扁平化带联合类型的TypeScript接口并在键中保留完整对象路径
TypeScript 嵌套接口扁平化实现
完整类型定义
// 辅助类型:判断对象属性是否为可选属性 type IsOptional<T, K extends keyof T> = {} extends Pick<T, K> ? true : false; // 辅助类型:合并交叉类型为单个对象,优化类型显示效果 type Merge<T> = { [K in keyof T]: T[K] } extends infer O ? O : never; // 辅助类型:处理单个类型的扁平化逻辑 type FlattenSingle<T, Path extends string = never> = // 非对象类型(原始类型、函数等)直接返回路径为键的对象 T extends object ? // 遍历对象所有属性 { [K in keyof T]-?: // 拼接当前属性的完整路径 (IsOptional<T, K> extends true ? // 可选属性生成带可选标记的扁平化结果 Partial<FlattenSingle<T[K], Path extends never ? `${K & string}` : `${Path}.${K & string}`>> : // 必选属性直接生成扁平化结果 FlattenSingle<T[K], Path extends never ? `${K & string}` : `${Path}.${K & string}`> ) // 合并所有属性的扁平化结果 }[keyof T] : // 原始类型返回对应路径的键值对 Path extends never ? never : { [key in Path]: T } // 主类型:支持联合类型的对象扁平化入口 type Flatten<T> = Merge< // 利用分布式条件类型处理联合,每个分支单独处理后合并 T extends any ? FlattenSingle<T> : never >
使用示例
针对你提供的接口测试效果:
interface Foo { foo: string bar?: number nested: { foo: string, deeplyNested: { bar?: number } } union: string | { foo: string, bar?: number } } // 生成的扁平化类型和你要求的效果完全一致 type FooFlattened = Flatten<Foo>
最终FooFlattened的展开结果为:
{ foo: string; bar?: number | undefined; "nested.foo": string; "nested.deeplyNested.bar"?: number | undefined; union: string; "union.foo": string; "union.bar"?: number | undefined; }
支持特性
- 支持任意层级的对象嵌套,自动拼接完整属性路径作为键名
- 自动处理所有联合类型成员,不同联合分支产生的扁平化键会合并到最终类型中
- 完整保留原接口的可选属性标记,可选属性扁平化后依然为可选状态
- 兼容所有基础原始类型(string/number/boolean等)
扩展说明
如果需要支持数组类型,可以在FlattenSingle类型中新增数组分支的处理逻辑,按需实现数组下标的路径拼接即可。
内容的提问来源于stack exchange,提问作者DaDo
相关产品推荐
相关产品推荐

