TypeScript 递归类型

递归类型指在类型定义中引用自身的类型,用来描述树、链表、JSON 这类天然无限嵌套的数据结构,也能写出 DeepReadonly、DeepPartial 等递归工具类型。掌握它才能表达真正"深"的结构。

递归对象类型:树节点

TreeNode<T> 的 children 类型再次引用 TreeNode<T>,形成无限层级的树:

interface TreeNode<T> {
  value: T;
  children?: TreeNode<T>[];
}

const tree: TreeNode<string> = {
  value: "root",
  children: [
    { value: "a" },
    { value: "b", children: [{ value: "b1" }, { value: "b2" }] },
  ],
};
console.log(tree.children?.[1]?.children?.[0]?.value); // 输出:b1

递归描述 JSON 数据

JSON 的值要么是基础类型,要么是数组或对象,而数组与对象内部又装 JSON 值:

type JsonValue =
  | string
  | number
  | boolean
  | null
  | JsonValue[]
  | { [key: string]: JsonValue };

const config: JsonValue = {
  name: "demo",
  port: 3000,
  debug: true,
  tags: ["web", "ts"],
  nested: { enable: false },
};
console.log(config); // 输出:完整 JSON 对象

递归工具类型

让映射类型在成员仍是对象时继续递归,即可实现深层只读:

type DeepReadonly<T> = {
  readonly [K in keyof T]: T[K] extends object ? DeepReadonly<T[K]> : T[K];
};

interface Config {
  db: { host: string; port: number };
}
const cfg: DeepReadonly<Config> = { db: { host: "localhost", port: 3306 } };
// cfg.db.port = 3307; // 编译错误:深层也只读
console.log(cfg.db.host); // 输出:localhost

递归条件类型

条件类型同样可以引用自身,例如把任意嵌套数组拍平成单层:

type Flatten<T> = T extends (infer E)[] ? Flatten<E> : T;

type A = Flatten<number>; // number
type B = Flatten<string[][]>; // string
type C = Flatten<[1, [2, [3]]]>; // 1 | 2 | 3(元组按联合合并)

深度与性能注意

递归必须有收敛条件,否则类型无限展开。TypeScript 对实例化深度有上限(超出会报"类型实例化过深"),因此递归层数要克制,能用一层工具就不要套多层。

小结

递归类型的本质是"类型引用自身 + 终止分支"。树、JSON、深层工具类型都靠它表达,写作时牢记三条:定义递归边界、控制展开深度、优先复用系统内置工具类型。

笔记加载中…