You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何解决递归扁平化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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.13 21:55:36