0Pricing
TypeScript Academy · 课时

树形结构的类型标注

使用递归类型对嵌套树节点进行建模。

树形结构的类型标注 是 CoddyKit 上的免费 TypeScript Academy 课时。 这是第 2 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 TypeScript Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 TypeScript Academy 课程共包含 4 节课。

树节点类型

树是链表的推广:每个节点都有一个值,以及一个由同类型子节点组成的数组。这是一种带有 children 数组的递归类型。

type TreeNode<T> = {
  value: T;
  children: TreeNode<T>[];
};
const leaf: TreeNode<number> = { value: 1, children: [] };
console.log(leaf.value);

构建一棵小树

带有子节点的节点就是嵌套对象。空数组是 leaf 的自然基础情况。

type TreeNode<T> = { value: T; children: TreeNode<T>[] };
const tree: TreeNode<string> = {
  value: "root",
  children: [
    { value: "a", children: [] },
    { value: "b", children: [] }
  ]
};
console.log(tree.children.length);

进一步嵌套

子节点可以拥有自己的子节点,深度不受限制,因为每个子节点本身都是完整的 TreeNode。

type TreeNode<T> = { value: T; children: TreeNode<T>[] };
const tree: TreeNode<number> = {
  value: 1,
  children: [
    { value: 2, children: [{ value: 4, children: [] }] },
    { value: 3, children: [] }
  ]
};
console.log(tree.children[0].children[0].value);

计算所有值的总和

递归函数会访问每个节点,并递归处理其子节点,同时累积结果,这是一种深度优先遍历。

type TreeNode<T> = { value: T; children: TreeNode<T>[] };
function sum(node: TreeNode<number>): number {
  let total = node.value;
  for (const child of node.children) total += sum(child);
  return total;
}
const t: TreeNode<number> = { value: 1, children: [{ value: 2, children: [] }, { value: 3, children: [] }] };
console.log(sum(t));

统计节点数量

相同的遍历结构也可以用于统计节点:当前节点计为一个,再加上所有子树的节点数量。

type TreeNode<T> = { value: T; children: TreeNode<T>[] };
function count<T>(node: TreeNode<T>): number {
  return 1 + node.children.reduce((acc, c) => acc + count(c), 0);
}
const t: TreeNode<string> = { value: "r", children: [{ value: "a", children: [] }] };
console.log(count(t));

查找最大深度

深度等于子节点中的最大深度加一;对于没有子节点的叶节点,深度为一。

type TreeNode<T> = { value: T; children: TreeNode<T>[] };
function depth<T>(node: TreeNode<T>): number {
  if (node.children.length === 0) return 1;
  return 1 + Math.max(...node.children.map(depth));
}
const t: TreeNode<number> = { value: 1, children: [{ value: 2, children: [{ value: 3, children: [] }] }] };
console.log(depth(t));

收集所有值

通过将当前值与子节点展平后的结果连接起来,将树展平为数组。

type TreeNode<T> = { value: T; children: TreeNode<T>[] };
function flatten<T>(node: TreeNode<T>): T[] {
  return [node.value, ...node.children.flatMap(flatten)];
}
const t: TreeNode<number> = { value: 1, children: [{ value: 2, children: [] }, { value: 3, children: [] }] };
console.log(flatten(t));

搜索树

递归搜索会返回第一个与谓词匹配的节点;如果当前节点不匹配,则继续探索子节点。

type TreeNode<T> = { value: T; children: TreeNode<T>[] };
function find<T>(node: TreeNode<T>, pred: (v: T) => boolean): TreeNode<T> | null {
  if (pred(node.value)) return node;
  for (const c of node.children) {
    const hit = find(c, pred);
    if (hit) return hit;
  }
  return null;
}
const t: TreeNode<number> = { value: 1, children: [{ value: 5, children: [] }] };
console.log(find(t, v => v === 5)?.value);

映射树

通过递归映射值和子节点,在保留结构的同时转换每个值。

type TreeNode<T> = { value: T; children: TreeNode<T>[] };
function mapTree<T, U>(node: TreeNode<T>, fn: (v: T) => U): TreeNode<U> {
  return { value: fn(node.value), children: node.children.map(c => mapTree(c, fn)) };
}
const t: TreeNode<number> = { value: 1, children: [{ value: 2, children: [] }] };
console.log(mapTree(t, x => x * 100).value);

树可以表示真实数据

文件系统、DOM 树、组织结构图和抽象语法树都是树。单个递归类型即可在保证完整类型安全的前提下描述它们。

type TreeNode<T> = { value: T; children: TreeNode<T>[] };
type FileTree = TreeNode<string>;
const fs: FileTree = { value: "/", children: [{ value: "home", children: [] }] };
console.log(fs.value, fs.children[0].value);

叶节点和分支节点

当 children 为空时,节点是叶节点;否则就是分支节点。这种区分通常会决定遍历逻辑。

type TreeNode<T> = { value: T; children: TreeNode<T>[] };
function isLeaf<T>(node: TreeNode<T>): boolean {
  return node.children.length === 0;
}
console.log(isLeaf({ value: 1, children: [] }));

快速检查:树结构

请测试您对树结构类型标注的理解。

回顾:树结构类型标注

您使用 TreeNode<T> 建模了树,其中每个节点都有一个值和一个子节点数组;随后编写了递归函数来对它们求和、计数、搜索和映射。

type TreeNode<T> = { value: T; children: TreeNode<T>[] };
const t: TreeNode<number> = { value: 1, children: [{ value: 2, children: [] }] };
console.log(t.children[0].value);

常见问题解答

「树形结构的类型标注」课时是免费的吗?

是的 — 「树形结构的类型标注」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 TypeScript Academy 课程的其余内容,请升级到 CoddyKit PRO。 TypeScript Academy 课程共包含 4 节课。

「树形结构的类型标注」这节课中我会学到什么?

使用递归类型对嵌套树节点进行建模。 你通过在浏览器中直接运行的动手代码来练习 TypeScript Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

学习 TypeScript Academy 需要有经验吗?

无需任何先前经验。CoddyKit 上的 TypeScript Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 2 节课,共 4 节。

「树形结构的类型标注」课时需要多长时间?

大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。

我能在这节 TypeScript Academy 课中编写并运行代码吗?

能。每节 TypeScript Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。

此课程中的所有课时

  1. 递归类型定义
  2. 树形结构的类型标注
  3. JSON 值类型
  4. 递归深度与限制
← 返回 TypeScript Academy