TypeScript如何定义单层扁平化对象的Flatten工具类型?
实现TypeScript单层扁平化工具类型
Flatten 需求分析
我们需要实现一个工具类型Flatten,满足以下要求:
- 接受两个泛型参数:目标对象类型
T(结构为Record<string, Record<string, unknown>>)、顶层键的集合K(联合或元组类型,需包含所有顶层键) - 按
K的顺序展开T中对应顶层键的对象属性,后续对象的同名属性会覆盖前面的 - 仅扁平化顶层结构,深层嵌套对象保持原样
实现代码
// 合并两个对象类型,后续对象的属性覆盖前置对象的同名属性 type Merge<Prev extends Record<string, unknown>, Next extends Record<string, unknown>> = Omit<Prev, keyof Next> & Next; // 递归合并元组中的所有对象类型,按从左到右的顺序累积结果 type MergeTuple< T extends Record<string, Record<string, unknown>>, Keys extends readonly (keyof T)[], Acc extends Record<string, unknown> = {} > = Keys extends [infer First extends keyof T, ...infer Rest extends (keyof T)[]] ? MergeTuple<T, Rest, Merge<Acc, T[First]>> : Acc; // 将交叉类型转换为普通对象类型,优化类型显示效果 type ToObject<T> = { [P in keyof T]: T[P] }; // 辅助类型:将联合类型转换为元组(顺序由TypeScript类型系统决定) type UnionToTuple<U> = (U extends any ? (x: U) => void : never) extends (x: infer T) => void ? T[] : never; // 主工具类型Flatten type Flatten< T extends Record<string, Record<string, unknown>>, K extends keyof T > = K extends readonly (keyof T)[] ? ToObject<MergeTuple<T, K>> : K extends keyof T ? ToObject<MergeTuple<T, UnionToTuple<K>>> : never;
代码说明
Merge类型:- 使用
Omit<Prev, keyof Next>移除前置对象中与后续对象重复的键,再与后续对象交叉,实现后续属性覆盖前置属性的效果,和Object.assign的行为一致。
- 使用
MergeTuple类型:- 通过递归遍历元组中的每个键,用
Acc参数累积合并结果,严格按元组顺序从左到右合并,保证后续对象的属性能覆盖前面的。
- 通过递归遍历元组中的每个键,用
ToObject类型:- 把交叉类型转换为普通的对象字面量类型,避免结果显示为复杂的交叉表达式,提升可读性。
UnionToTuple类型:- 将联合类型转换为元组,让联合类型也能复用
MergeTuple的合并逻辑,虽然联合类型本身无序,但能保证所有顶层键对应的属性都被合并。
- 将联合类型转换为元组,让联合类型也能复用
测试示例
定义示例类型:
type Example = { body: { user: { name: string; age: number; } }, query: { id: string; strict?: boolean; }, params: { id: number; scope: string[]; } };
调用Flatten<Example, 'query' | 'params' | 'body'>或Flatten<Example, ['query', 'params', 'body']>,都会得到目标类型:
{ id: number; strict?: boolean; scope: string[]; user: { name: string; age: number }; }
内容的提问来源于stack exchange,提问作者Errol59
相关产品推荐
相关产品推荐

