TypeScript 7.0 实测:Go 重写的编译器快 10 倍,牛马的 CI 终于能喘口气了
背景与热点吐槽
上周我正在给一个 30 多万行的 monorepo 跑 CI,类型检查那一步照例挂着 4 分多钟,我照例起身去接水。回来的路上刷到消息:TypeScript 7.0 正式发布了,Go 重写的原生编译器 tsgo 落地,官方说大型项目类型检查和构建普遍提速约 10 倍。
我第一反应不是兴奋,是警惕。前端牛马都懂:凡是"重写"两个字出现,后面必然跟着"迁移成本"四个字。当年 Webpack 4 到 5、Vue 2 到 3,哪次不是官方说"平滑升级",最后我们在工位上平滑地加了三个通宵。
但这次我认真看完发布说明之后,态度软了。因为微软干了一件很克制的事:tsgo 的类型检查逻辑和 TypeScript 6.0 保持结构一致,也就是说它不是"新语言",而是"同一套规则换了个跑得快的裁判"。你的代码在 TS 6 下报什么错,在 TS 7 下大概率还是报什么错,只是快了一个数量级。这个升级姿势,比某些框架大版本厚道多了(点名不批评,Vue 老铁们懂的)。
技术原理拆解
为什么快 10 倍?不是 Go 比 JavaScript 天生快 10 倍这么简单,主要是三件事叠加:
1. 原生编译 + 真并发。 老的 tsc 是用 TypeScript 写的,跑在 Node.js 单线程上,几十万行代码的类型检查只能一个文件一个文件串行啃。tsgo 是 Go 写的原生二进制,天然支持多核并行:程序解析、绑定、类型检查可以按文件/项目粒度分发到多个 goroutine。你的 8 核 CI 机器终于不是只有一个核在 996,其他七个在带薪摸鱼了。
2. 内存布局优化。 JS 里一个 AST 节点是一个对象,对象头、隐藏类、GC 压力全是开销。Go 版本可以用紧凑的结构体和数组存储 AST,缓存命中率高,GC 负担小。对于大仓库,这部分收益甚至比并发还大。
3. 原生 LSP。 以前 VS Code 里的 TS 语言服务也是跑在 Node 上的,大 monorepo 打开一个文件,"Loading..."能转到你怀疑人生。TS 7 的语言服务是原生进程 + 新 LSP 实现,冷启动和跳转定义的延迟改善非常明显。这一点对日常幸福感的提升,其实比 CI 提速更直接——CI 慢我可以摸鱼,编辑器卡是真的想砸键盘。
代价是什么?JS 侧的 Compiler API 不再是免费午餐。 老 tsc 是个 npm 包,任何工具都能 import ts from "typescript" 拿到完整编译器 API,于是长出了 ts-jest、typescript-eslint、vue-tsc、各种自定义 transformer 这一整片生态。tsgo 是外部二进制,JS 进程没法直接嵌入它的内部对象,深度依赖 compiler API 的工具都需要适配期。这就是升级的主要坑位。
代码示例或实操演示
我在一个 Next.js 项目和一个 Vue 3 项目上各试了一轮,流程记录如下。
第一步:安装并做类型检查对比。
npm i -D typescript@7
# 老项目先别急着全量切,单独跑一次对比
npx tsc --noEmit
我那个 Next.js 中台项目(约 12 万行),tsc --noEmit 从 96s 降到 11s;Vue 项目要走 vue-tsc,暂时还没吃到全部收益(后面说)。
第二步:清理废弃编译选项。 TS 6/7 清掉了一批老古董,先自查 tsconfig.json:
{
"compilerOptions": {
// 以下选项已废弃/移除,升级前删掉或替换:
// "charset": "utf8",
// "out": "./dist/bundle.js", // 用 outFile / outDir
// "importsNotUsedAsValues": "error", // 改用 verbatimModuleSyntax
// "preserveValueImports": true, // 同上
// "suppressImplicitAnyIndexErrors": true
"verbatimModuleSyntax": true,
"strict": true
}
}
第三步:CI 双轨灰度。 我不敢一把梭,就在流水线里让 TS 7 先"实习"两周:
jobs:
typecheck-ts7:
steps:
- run: npx tsc --noEmit # typescript@7,主流程,失败即挡合并
typecheck-ts6-canary:
continue-on-error: true # TS6 降级为参考岗,只报警不拦截
steps:
- run: npx tsc6 --noEmit # 通过别名装的 typescript@6
两周内两边结论完全一致后,我就把 TS 6 那条腿撤了。
牛马实践建议
结合两个项目的实战,给同样在搬砖的各位几条真心话:
1. 纯应用项目,现在就可以上。 如果你的项目只是"写 TS、跑 tsc、打包用 esbuild/SWC/Vite",那你对 compiler API 没有深度依赖,升级基本等于白捡 10 倍速。尤其是 Next.js 项目——next build 里那步类型检查快了之后,整体构建时长肉眼可见地掉下来。
2. Vue 玩家看住 vue-tsc。 vue-tsc 是挂在 TS compiler API 上做 SFC 类型检查的,属于受影响最深的一类工具。升级前去看 Volar/vue-tsc 的适配进度,别看到 10 倍就无脑冲,冲完发现 .vue 文件类型检查跑不起来,那就是把自己架在火上烤。我的 Vue 项目目前策略是:业务代码的 .ts 用 TS 7 检查,SFC 检查暂留旧链路,等适配版稳定再合并。
3. typed-lint 是隐藏耗时大户。 typescript-eslint 的类型感知规则(recommended-type-checked)同样吃 compiler API,它的适配节奏决定你 lint 那一步能不能同步提速。如果暂时不行,lint 和 typecheck 分开跑,别互相拖累。
4. 别忘了给团队算账。 我给老板算过:CI 类型检查每天跑 80 次,每次省 85 秒,一天省接近 2 小时机器时间,人等 CI 的碎片时间更值钱。这种升级是少数"技术爽、老板也认"的活,建议主动立项,别等别人抢了。
5. 锁死版本,留好回滚。 package.json 里精确锁 "typescript": "7.0.2",出问题一行 lockfile 回滚。成年人的世界没有勇气,只有回滚方案。
总结与展望
说实话,这几年前端圈的"Rust/Go 重写运动"我一直半信半疑——很多项目重写完性能是上去了,生态碎了一地。但 TS 7 这次给我的感觉不一样:它把"行为与 TS 6 一致"当成第一优先级,把提速当成副产品送给你,这是对存量生态最大的尊重。
往后看,原生 LSP 会成为新的基建层,vue-tsc、typescript-eslint 这些工具迟早都会迁到新接口上,阵痛期估计半年左右。我的建议是:应用项目现在上车吃红利,工具链作者和重度插件用户再观望一两个 minor 版本。
至于我?CI 从 4 分钟变成 30 秒之后,我失去了每天七八次光明正大的接水时间。快是真的快,摸鱼是真的没得摸了。卷不动,但还得卷,下班。
FAQ
TypeScript 7.0 会不会导致我的代码报新的类型错误?
基本不会。官方明确 tsgo 的类型检查逻辑与 TypeScript 6.0 结构一致,同一份代码的报错行为保持兼容。你需要处理的主要是 tsconfig 中已废弃的编译选项(如 importsNotUsedAsValues、out 等),而不是业务代码本身。稳妥起见可以在 CI 里 TS6/TS7 双轨跑两周对比结论。
为什么 Go 重写能快约 10 倍,而不是只快一点?
三个因素叠加:一是原生二进制摆脱了 Node.js 单线程限制,类型检查可多核并行;二是 Go 可以用紧凑的结构体和数组组织 AST,内存局部性好、GC 压力小;三是解析、绑定、检查各阶段整体重新设计。单看语言差异可能只有 2-3 倍,并发和内存布局才是大头,所以项目越大、核越多,收益越明显。
Vue 项目用 vue-tsc 做 SFC 类型检查,现在能升 TS 7 吗?
建议分步走。vue-tsc 深度依赖 TypeScript compiler API,属于受 tsgo 架构变化影响最大的工具之一,需等 Volar 生态发布适配版本。过渡期可以让纯 .ts 业务代码走 TS 7 检查享受提速,.vue 文件的类型检查暂留旧链路,等适配稳定后再统一切换。
升级 TS 7 对 Vite、esbuild、SWC 这类构建工具有影响吗?
几乎没有。这些工具本来就只做 TS 语法剥离(transpile),不调用 tsc 做类型检查,所以不受 tsgo 影响。受影响的是依赖 compiler API 的工具:ts-jest、typescript-eslint 的类型感知规则、vue-tsc、自定义 transformer 等,升级前逐一确认它们的兼容版本即可。
编辑器里怎么用上 TS 7 的原生 LSP?
VS Code 用户升级 TypeScript 扩展并在设置中启用原生语言服务(工作区 TypeScript 版本指向项目内 typescript@7)即可,大型 monorepo 的冷启动、跳转定义和自动补全延迟改善非常明显。其他基于 LSP 的编辑器(Neovim、Zed 等)跟进各自的 LSP 客户端配置即可接入。