</> 代码前沿Code Frontier

TypeScript 7:当编译器不再是 JavaScript

2026-10-12 编译器 约 14 分钟 涉及版本 TS 6.0 / 7.0.2

2026 年 7 月 8 日,tsc 这个命令在十多年后第一次不再运行 JavaScript。 TypeScript 7.0 的编译器用 Go 重写,微软给出的真实代码库基准是 8 到 12 倍的全量构建提速。 但这次升级和以往任何一次都不同:语言没变,工具链却断了一截。

这篇文章不复述发布公告,而是回答三个更实际的问题:那些倍数是怎么来的、 为什么大量工具现在还不能升到 7、以及你的项目应该按什么顺序动。

先看数字,再看数字的上下文

微软公布的基准用的是公开代码库,而非合成测试,这点值得肯定。原始数据大致如下:

代码库TS 6.0TS 7.0提升
VS Code(全量类型检查)125.7 s10.6 s约 11.9×
Sentry139.8 s15.7 s约 8.9×
Playwright12.8 s1.47 s约 8.7×
VS Code 编辑器报错首现约 17.5 s< 1.3 s约 13×

这些数字是真的,但有几个前提需要说清楚,否则容易期待落空:

  • 提升幅度和项目规模正相关。 小项目本来就只花几百毫秒,压缩到几十毫秒在体感上几乎没有差别。 收益最大的是"全量检查要等一分钟以上"的中大型 monorepo。
  • 这是全量(clean build)数据。 日常增量编译和 watch 模式本来就有缓存,改善幅度通常小于全量构建。
  • 并行度相关。 7.0 把解析、检查、产物生成拆到多线程,核心数少的机器拿到的增益主要来自"原生代码本身更快"这部分,而不是并行。
值得注意

微软同时提到内存占用下降约 18%(VS Code 从 5.2 GB 降到 4.2 GB)。 对 CI 机器来说这个变化可能比速度更有价值 —— 它直接决定你能在同样规格的 runner 上跑多少个并行任务。

Go 为什么能快一个数量级

原因不是"Go 比 JavaScript 快十倍"这么简单的比较。真正的来源有三块:

1. 没有 JIT 预热与 GC 压力

原实现跑在 Node.js 上,冷启动要先解析、编译、预热几十万行 JavaScript。 这是为什么"打开一个文件要等十几秒"——大部分时间不在类型检查,而在让运行时热起来。 Go 编译成原生二进制,启动即全速。

2. 真正的共享内存多线程

Node.js 的单线程模型决定了原编译器只能靠 worker 进程做粗粒度并行,进程间传数据要序列化。 7.0 使用共享内存的多线程,类型检查可以按项目切分到多个 worker:

terminal
# 每个项目 8 个 checker worker
npx tsc --checkers 8 --noEmit -p tsconfig.json

# 多项目构建:4 个项目并行,每个项目 4 个 checker(最多 16 个)
npx tsc --build --builders 4 --checkers 4

# 单线程模式:用于排查顺序相关的诡异结果
npx tsc --singleThreaded --noEmit -p tsconfig.json

这三个参数在 7.0 中标注为实验性。--checkers 默认是 4; 调高会占用更多内存,且在极少数情况下可能出现"顺序相关"的结果差异 —— 所以官方保留了 --singleThreaded 作为排查手段。

3. 移植而非重写

这是最关键的一点。团队没有借机重新设计类型系统,而是把原有逻辑逐段翻译成 Go, 目标是与 6.0 的类型检查行为保持一致。内部代号 Strada(旧 JS 代码库)与 Corsa(新 Go 代码库), 两者刻意保留的差异记录在一个 CHANGES.md 文件里。

对你的意义是:升级不会让原本通过的类型检查突然失败(除了配置项被移除的情况)。 这是一个罕见的、语言语义完全不变的性能升级。

真正的麻烦:7.0 没有 compiler API

7.0 最容易被忽略、影响却最大的事实是:它不提供对外编程接口。 新的 API 预计在 7.1 提供。

这意味着所有"以库的形式调用 TypeScript"的工具都无法运行在 7.0 上:

工具类别代表7.0 状态
Lint / 静态检查 typescript-eslint 需留在 6.0
框架模板检查 Vue、Svelte、Astro、Angular 需留在 6.0
代码生成 / 转换 ts-morph 及各类 codemod 需留在 6.0
纯命令行编译 tsc 本身 可直接使用 7.0

于是出现了一个很容易踩的坑:从 2026 年 7 月起,npm 上 typescript 的 latest 标签已经指向 7.x。执行 npm i -D typescript 装到的是原生编译器, 而如果你的 lint 流程依赖 API,它会在这一步突然坏掉。想留在 6.0 必须显式指定:

package.json 片段
# 锁定 6.x
npm i -D typescript@6

# 或者并列安装:让 lint 用 6,构建用 7
npm i -D typescript@7 @typescript/typescript6

# 验证实际生效的编译器版本(重要)
npx tsc --version
已知陷阱

@typescript/typescript6 会带一个名为 tsc 的二进制, 与 typescript@7 的同名二进制冲突。 npm 会把后写入的那个链到 node_modules/.bin, 结果可能是你以为装了 7,实际跑的还是 6。

所以上面最后一行不是可选项:每次安装后都打印一次版本, 确认 CI 里跑的到底是哪个编译器。

被移除的配置项

6.0 里所有标记弃用的选项,在 7.0 中变成硬错误。这不是提醒,是直接编译失败。 受影响的常见项:

  • target: "es5"
  • baseUrl 作为模块查找根目录
  • moduleResolution: "node" / "node10"
  • AMD、UMD、SystemJS 三种模块格式

另外 6.0 改了若干默认值,这些即使你不改 tsconfig 也会生效, 是升级时"报错最多"的来源:

选项旧默认6.0 起的默认
strictfalsetrue
module随 target 变化esnext
targetes5最新 ES 版本
types所有 @types 包空数组
noUncheckedSideEffectImportsfalsetrue

其中 types 默认为空数组最容易造成困惑:@types/node、 @types/jest 不再被自动引入,症状是一堆 Cannot find name 'process'、 Cannot find name 'describe'。解决方式是显式声明:

tsconfig.json
{
  "compilerOptions": {
    "target": "es2024",
    "module": "esnext",
    "moduleResolution": "bundler",
    "strict": true,
    "verbatimModuleSyntax": true,

    // 6.0 起必须显式列出,否则不加载
    "types": ["node", "vitest/globals"]
  }
}

推荐的迁移顺序

把 6.0 当成"清场版本"用,是风险最低的路径。分四步:

第一步:先上 6.0,把所有弃用项清掉

这一步不追求变快,追求的是 tsc --noEmit 在没有 "ignoreDeprecations": "6.0" 兜底的情况下零报错。 在 6.0 上有兜底选项可用,到了 7.0 就没有了。

第二步:CI 里并行跑 6 和 7

构建用 7 计时,lint 仍走 6。两边都绿,再考虑切换。这样能提前发现 "行为差异"与"工具缺失"两类问题:

.github/workflows/typecheck.yml
jobs:
  typecheck:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: 原生编译器(计时用)
        run: |
          npx -y typescript@7 tsc --noEmit -p tsconfig.json
          npx -y typescript@7 tsc --version

      - name: 6.0 + API 工具(lint 用)
        run: |
          npm ci
          npx tsc --version
          npm run lint

第三步:确认工具链的 7.1 支持情况

关注三类工具:lint、框架模板检查、基于 API 的代码生成。 它们不支持 7.x 之前,不要动 CI 的主编译路径。

第四步:全量切换,回收 CI 时间

等到 API 到位、工具跟进,再把默认编译器换成 7。 真正的收益此刻才兑现 —— 尤其是那些因为"全量检查太慢"而只跑增量、把问题留到合并后才发现的项目。

这次升级该怎么看

TypeScript 7 是一次罕见的"纯性能"升级:类型语义不变,语言不变,改的只是引擎。 对长期忍受大 monorepo 编译等待的团队,这是数年来最实在的一次改善。

但它同时暴露了一件事:TypeScript 早已不只是编译器,而是一套被大量工具依赖的基础设施。 换引擎意味着这些工具需要一个稳定的接口才能跟上,而接口比引擎慢了一拍。 这不是微软的失误,而是生态型项目升级时的结构性代价。

判断要不要现在升,只需回答一个问题:你的构建流程里,有多少环节是以库的形式调用 TypeScript, 而不是调用 tsc 命令?如果是零,现在就能升;如果不是,先清配置,等 7.1。
参考来源
  1. TypeScript 6.0 升级说明与发布时间线(含 6.0 默认值变更、弃用项清单)
  2. TypeScript 7.0 发布公告与 Project Corsa 基准数据(VS Code / Sentry / Playwright 构建耗时、内存变化)
  3. TypeScript 7 迁移实践指南(--checkers、--builders、tsc6 包与二进制冲突说明)
  4. 生态影响分析(typescript-eslint、Vue、Svelte、Astro、Angular 对 compiler API 的依赖)