递归联合类型实现:仅单分支属性可选(支持嵌套对象)
递归处理联合类型的嵌套属性合并与可选化
针对递归需求,我们可以通过遍历类型的每个属性层级,将联合类型中各层级的对象都合并为「共享属性保留,仅部分分支存在的属性设为可选」的结构。以下是完整的类型工具实现:
// 获取联合类型所有分支的全部键名 type AllKeys<T> = T extends unknown ? keyof T : never; // 判断类型是否为非空对象(排除null、数组等) type IsObject<T> = T extends object ? T extends null | Array<any> ? false : true : false; // 递归合并联合类型的每个层级 type MergeUnionRecursive<T> = T extends object ? { [K in AllKeys<T>]: // 对每个键,收集所有分支对应的类型并递归处理 MergeUnionRecursive< T extends { [P in K]?: infer V } ? V : never > } extends infer Merged ? // 过滤掉类型为never的属性,再将仅部分分支存在的属性转为可选 { [K in keyof Merged as Merged[K] extends never ? never : K]: Merged[K] extends never ? never : Merged[K] } extends infer Final ? Final & Partial< Record< Exclude<AllKeys<T>, keyof Final>, never > > extends infer Combined ? Omit<Combined, never> : never : never : T; // 最终的递归可选化工具类型 type OptionalizeRecursive<T> = MergeUnionRecursive<T>;
测试示例
基础嵌套场景
type X1 = { shared: { x1: number }, x: number }; type Y1 = { shared: { y1: string }, y: number }; type Z1 = OptionalizeRecursive<X1 | Y1>; /* 最终结果: type Z1 = { shared: { x1?: number | undefined; y1?: string | undefined; }; x?: number | undefined; y?: number | undefined; } */
多层嵌套场景
type A = { top: { mid: { a: string }, onlyA: boolean } }; type B = { top: { mid: { b: number }, onlyB: string } }; type MergedAB = OptionalizeRecursive<A | B>; /* 最终结果: type MergedAB = { top: { mid: { a?: string | undefined; b?: number | undefined; }; onlyA?: boolean | undefined; onlyB?: string | undefined; }; } */
关键类型说明
AllKeys<T>:遍历联合类型的每个分支,收集所有可能的键名,确保不会遗漏任何分支的属性。IsObject<T>:用于判断当前属性值是否为需要递归处理的对象类型,避免对原始类型(如string、number)进行不必要的递归。MergeUnionRecursive<T>:核心递归逻辑,先对每个键的对应类型做递归合并,再过滤无效的never类型属性,最后将仅部分分支存在的键转为可选,得到干净的合并结构。
内容的提问来源于stack exchange,提问作者Alexey Romanov
相关产品推荐
相关产品推荐

