TypeScript 7.0 用上了:编译器换 Go 之后,构建时间从等一杯咖啡变成了等一个深呼吸
TypeScript 7.0 把编译器用 Go 重写了一遍,构建速度提升 8-12 倍。这篇文章记录了我在一个中型 Next.js 项目上的实际迁移体验:改了什么、踩了什么坑、哪些团队现在能升、哪些得等等。
7 月 8 日,微软发布了 TypeScript 7.0。不是一个大版本号下塞了一堆新语法特性,而是把整个编译器从 TypeScript 移植到了 Go。
这意味着什么?你用 npm install -D typescript 装回来的不再是跑在 Node.js 上的 JavaScript 编译器,而是一个原生 Go 二进制。
不是重写,是移植
TypeScript 团队的说法很精准——"methodically ported, not rewritten from scratch"。他们没有在 Go 里重新设计类型检查器的逻辑,而是把 TS 6.0 的类型检查代码一行一行"翻译"成了 Go。所以类型推断行为、错误信息、你熟悉的那些边界情况,理论上和 6.0 完全一样。
微软用十年的测试套件(大约 20000 个测试用例)验证了这一点:在所有测试中,TS 7 和 TS 6 只有 74 个用例产出了不同的错误——而且大多数差异是故意为之的行为变更,不是 bug。
换句话说,这不是一个"新编译器",而是一个"换了个更快的引擎的老编译器"。
到底快了多少
官方发布的五组基准数据:
| 项目 | TS 6 | TS 7 | 提速 |
|---|---|---|---|
| VS Code | 125.7s | 10.6s | 11.9x |
| Sentry | 139.8s | 15.7s | 8.9x |
| Bluesky | 24.3s | 2.8s | 8.7x |
| Playwright | 12.8s | 1.47s | 8.7x |
| tldraw | 11.2s | 1.46s | 7.7x |
VS Code 的完整类型检查从两分多钟变成了十秒出头。这个差距不是"快了",是类型检查从一个需要专门安排时间的事情变成了可以挂在每次 commit 之前的东西。
我在自己一个中等规模的 Next.js 项目上试了下。tsc --noEmit 之前大概 45 秒,换到 7.0 之后稳定在 4-5 秒。以前习惯先 push 再等 CI 报类型错误,现在本地跑一下几乎没什么感知成本。
除了构建时间,内存也有明显下降。微软公布的数据是 6% 到 26% 不等,取决于项目结构。如果你在 CI 里跑类型检查,内存峰值降低意味着可以用更小的 runner,直接省钱。
编辑器体验的变化
构建快是 CI 里的账,但编辑器响应速度是每天几百次的事情。TS 7 的语言服务同样换成了 Go 实现,通过 LSP 协议和编辑器通信。
VS Code 加载整个 VS Code 项目时,语言服务就绪时间从 9.6 秒降到了 1.2 秒。在我自己的项目里,打开文件后红色波浪线的出现时间从"等我切个窗口再切回来"变成了"几乎是即时的"。
而且微软说 TS 7 的语言服务崩溃率比 6.0 降低了超过 20 倍。这一点我在一周的使用中确实感受到了——没有遇到过一次语言服务挂掉需要重启的情况,而 6.x 时代偶尔会遇到。
迁移踩到的坑
升 7.0 不是改个版本号就完事。TS 7 把 6.0 里的 deprecation 全部变成了硬错误,而且默认配置变得更严格了。
strict 默认为 true
如果你之前没开 strict,升到 7.0 之后会多出一堆类型错误。好消息是这些错误本来就应该修,坏消息是修起来可能不少。
target 不能再设为 ES5
target: "es5" 直接报错。ES2015 是最低要求。如果你的项目还在编译到 ES5 来兼容老浏览器,现在必须至少升到 ES2015,然后用 bundler 做 polyfill。
module 选项大瘦身
amd、umd、system、none 全部移除。推荐用 esnext,然后让 bundler 处理输出格式。对绝大多数项目来说这其实没影响——本来就没几个人还在用 AMD。
moduleResolution 清理
node、node10、classic 全部移除,只保留 nodenext 和 bundler。这个改动对老旧项目影响较大,尤其是那些 paths 配置依赖 baseUrl 的项目——因为 baseUrl 也被移除了。迁移方案是把 paths 改成相对于项目根目录。
rootDir 默认值变了
rootDir 现在默认是 ./,如果你的 tsconfig.json 放在项目根目录但源码在 src/ 下,需要显式设置 "rootDir": "./src"。
types 默认变为空数组
types 现在默认是 [],意味着 @types/* 包不再自动引入。如果你的项目依赖 @types/node、@types/jest 等全局类型声明,需要显式列出来:"types": ["node", "jest"]。
哪些团队现在能升,哪些得等等
现在能升的:
- 用 Next.js、Remix、Vite 等现代框架的项目——这些框架通常不直接依赖 TS 的程序化 API
- 纯
tsc --noEmit做类型检查的 CI 流程 strict: true已经开了的项目- 没有
target: "es5"需求的项目
需要等 7.1 的:
- 依赖
typescript-eslint的项目——它需要 TS 的程序化 API,7.0 里这个 API 还没稳定 - Vue、Svelte、Angular 的模板类型检查——这些框架的编译器直接调用 TS API
- Astro、MDX 等依赖 TS API 做类型处理的工具链
- 任何通过
import typescript from "typescript"做插件或工具开发的场景
微软的做法是发了一个 @typescript/typescript6 兼容包,让你可以同时装 6.0 和 7.0。编辑器用 7.0 获得流畅体验,CI 和工具链继续用 6.0,等 7.1 出来再全量切换。
为什么选 Go 而不是 Rust
这是 2025 年 3 月宣布移植计划时大家问得最多的问题。Anders Hejlsberg 的解释很务实:TypeScript 编译器做的事主要是遍历 AST 图、做类型运算,这种工作模式用 Go 写起来很自然,和原来的 TypeScript 代码风格也接近。再加上 Go 有 GC、协程和原生编译,刚好覆盖了编译器需要的所有特性,还不用引入 Rust 的所有权系统带来的心智负担。
还有一个很实际的原因:Go 写出来的代码结构和原来的 TypeScript 代码库比较像,让"翻译"而不是"重写"这件事变得可行。如果选 Rust,团队很可能会被迫重新设计大量内部架构,工期就不是一年了。
这次升级到底意味着什么
TS 7.0 给 TypeScript 生态带来的变化不只是"快了"。当类型检查从一个需要专门排时间的步骤变成几乎无感的背景任务,整个开发流程会跟着变:
- CI 里
tsc --noEmit从最慢的步骤变成能跟 lint 和测试并列的快速检查 --watch模式真正变得可用——之前的 watch 在大项目上经常跟不上文件变更- 编辑器的类型反馈延迟降到接近零,写代码的节奏感完全不同
- 内存占用降低让本地开发和 CI runner 的成本都往下走
对我来说,最直观的变化是:以前敲完代码习惯性按保存然后等一两秒看有没有红线,现在是保存完红线就已经在了。这种"无感"本身就是最好的体验。
还没升的话,可以先在编辑器里装 TypeScript Native Preview 扩展 试一下,不影响你现有的构建流程,但编辑体验直接就是 7.0 的了。