如何解决递归扁平化TypeScript类型时的无限实例化错误
解决TypeScript递归扁平化并排除Row/Column类型的问题
你遇到的「类型实例化过深且可能无限」错误,核心原因是Row和Column被定义为恒等类型别名(type Column<T> = T),TypeScript无法区分Row<T>和原始的T,导致ExcludeType递归时无限调用自身(因为T extends Row<infer U>会推断U=T,进而触发无限递归)。同时原类型仅实现了解开包装的逻辑,未做对象属性扁平化,无法得到期望的扁平结构。
步骤1:修正Row/Column的定义
要让TypeScript识别Row和Column类型,需给它们添加独特标识结构,比如:
type Column<T> = { __column: true; data: T }; type Row<T> = { __row: true; data: T };
通过__column/__row属性,TypeScript能明确区分包装类型与普通对象。
步骤2:编写递归扁平化类型
我们需要实现两个核心逻辑:递归解开Row/Column包装、扁平化嵌套对象属性,同时添加终止条件避免无限递归:
// 递归解开Row/Column包装 type UnwrapRowColumn<T> = T extends Row<infer U> ? UnwrapRowColumn<U> : T extends Column<infer U> ? UnwrapRowColumn<U> : T; // 扁平化嵌套对象 type FlattenObject<T> = T extends object ? { [K in keyof T]: FlattenObject<UnwrapRowColumn<T[K]>> } extends infer O ? { [K in keyof O]: O[K] extends object ? FlattenObject<O[K]> : O[K] } extends infer Flat ? // 合并所有非对象属性与嵌套对象的属性 { [P in keyof Flat as Flat[P] extends object ? never : P]: Flat[P] } & { [P in keyof Flat as Flat[P] extends object ? keyof FlattenObject<Flat[P]> : never]: FlattenObject<Flat[P]>[keyof FlattenObject<Flat[P]>] } extends infer Merged ? { [K in keyof Merged]: Merged[K] } : never : never : T; // 合并逻辑的最终类型 type ExcludeAndFlatten<T> = FlattenObject<UnwrapRowColumn<T>>;
测试验证
用你的Form结构测试,得到期望的扁平结果:
interface Form { row1: Row<{ col1: Column<{ field1: number; }>; col2: Column<{ field2: string; }>; }>; } type FilteredType = ExcludeAndFlatten<Form>; // 结果:{ field1: number; field2: string }
应对深度嵌套场景
如果实际结构嵌套极深,可添加递归深度限制避免报错:
// 带深度限制的解包装类型 type UnwrapRowColumnWithDepth<T, Depth extends number = 10> = Depth extends 0 ? T : T extends Row<infer U> ? UnwrapRowColumnWithDepth<U, Depth extends 0 ? 0 : Depth - 1> : T extends Column<infer U> ? UnwrapRowColumnWithDepth<U, Depth extends 0 ? 0 : Depth - 1> : T; // 带深度限制的扁平化类型 type FlattenObjectWithDepth<T, Depth extends number = 10> = Depth extends 0 ? T : T extends object ? { [K in keyof T]: FlattenObjectWithDepth<UnwrapRowColumnWithDepth<T[K], Depth>, Depth extends 0 ? 0 : Depth - 1> } extends infer O ? { [K in keyof O]: O[K] extends object ? FlattenObjectWithDepth<O[K], Depth extends 0 ? 0 : Depth - 1> : O[K] } extends infer Flat ? { [P in keyof Flat as Flat[P] extends object ? never : P]: Flat[P] } & { [P in keyof Flat as Flat[P] extends object ? keyof FlattenObjectWithDepth<Flat[P], Depth extends 0 ? 0 : Depth - 1> : never]: FlattenObjectWithDepth<Flat[P], Depth extends 0 ? 0 : Depth - 1>[keyof FlattenObjectWithDepth<Flat[P], Depth extends 0 ? 0 : Depth - 1>] } extends infer Merged ? { [K in keyof Merged]: Merged[K] } : never : never : T; type ExcludeAndFlatten<T, Depth extends number = 10> = FlattenObjectWithDepth<UnwrapRowColumnWithDepth<T, Depth>, Depth>;
内容的提问来源于stack exchange,提问作者cyrus-d
相关产品推荐
相关产品推荐

