TypeScript 隔离声明:提升 Monorepo 构建速度

Monorepo 的构建速度经常会因为 TypeScript 处理声明文件的方式而变慢。默认情况下,TypeScript 在创建单个 .d.ts 文件之前,会分析每一个导入链。这导致了一个漫长的等待队列,各个包必须互相等待。

你可以通过 --isolatedDeclarations 标志来解决这个问题。

该标志允许并行构建。每个包不再需要等待依赖项,而是独立生成自己的声明。团队可以实现 3 到 15 倍的构建速度提升。

问题所在: TypeScript 通常会在模块边界之间进行类型推断。如果包 A 依赖于包 B,编译器必须在开始处理 A 之前完全解析 B。在大型 monorepo 中,当编译器逐个处理时,大多数包都处于闲置状态。

解决方案: --isolatedDeclarations 标志改变了规则。它要求你为你导出的所有内容编写显式的类型注解。

折衷方案: 你的代码量会增加。你必须手动为函数添加返回类型,并为常量添加类型。你不能再依赖编译器从代码中猜测这些类型。

结果: 由于每个导出项都有明确的类型,编译器无需查看其他包即可理解它们。它可以同时处理每一个包。

实际效果:

  • 一个拥有 18 个包的 monorepo,构建时间从 47 秒降至 3.2 秒。
  • 一个拥有 32 个包的 monorepo,在 CI 流水线上的速度提升了 8 倍。
  • 性能随 CPU 核心数扩展。核心越多,意味着可以同时构建更多的包。

如何迁移:

  1. 在根目录的 tsconfig.json 和每个包的配置中启用该标志。
  2. 使用 "composite" 标志以允许项目引用(project references)。
  3. 为导出的函数添加显式的返回类型:

// 之前 export function add(a: number, b: number) { return a + b; }

// 之后 export function add(a: number, b: number): number { return a + b; }

  1. 为导出的常量添加显式类型:

// 之前 export const SETTINGS = { port: 3000 };

// 之后 export const SETTINGS: { port: number } = { port: 3000 };

如果你不添加这些类型,编译器将会报错。这确保了你的类型是确定性的且高效的。

来源:https://dev.to/jsmanifest/typescript-isolated-declarations-real-world-performance-gains-in-monorepo-build-times-25g3