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、深层工具类型都靠它表达,写作时牢记三条:定义递归边界、控制展开深度、优先复用系统内置工具类型。