TypeScript 类型检查,正在从等待时间变成无感操作。
一份 GitHub 基准把六个真实开源项目拉出来跑完整类型检查。老版 tsc 6 检查 375 万行的 VS Code,要 54.56 秒。
换成 Bun check,只要 1.62 秒,快了 33.7 倍。
换成 Rust 重写的 tsc-rs,是 4.20 秒,快了 13 倍。
更刺激的是,评论区出现了一名 TypeScript 团队成员。
TypeScript team member here - this is impressive work, and it's honestly crazy that 3 of these ports have popped up in the last week!
一周内冒出三个移植版。老实讲,这速度有点像编译器圈突然开卷。
Rust 版这次,明显是来抢位的
这次的主角叫 ts-rust,也叫 tsc-rs。
它是 TypeScript 编译器的 Rust 移植版。素材里的基准把它和三个对手放在一起:JavaScript 版 tsc 6,Go 版 tsc 7,Bun 的 bun check。
真实应用测试里,几何平均速度很直白。
tsc 7 比 tsc 6 快 7.1 倍。
tsc-rs 比 tsc 6 快 11.4 倍。
bun check 比 tsc 6 快 20.9 倍。
如果拿 tsc 7 当参照,tsc-rs 还要再快 1.61 倍,bun check 快 2.95 倍。
几个名字放出来更有画面感。
VS Code,375 万行。tsc 6 要 54.56 秒,tsc 7 压到 6.84 秒,tsc-rs 再压到 4.20 秒,bun check 直接冲到 1.62 秒。
Sentry 前端,211 万行。tsc 6 要 58.76 秒,tsc-rs 4.46 秒,bun check 3.14 秒。
Playwright,58.5 万行。tsc 6 要 4.48 秒,tsc-rs 0.34 秒,bun check 0.18 秒。
说实话,看到这种表,第一反应很容易变成语言阵营狂欢。
但个人觉得,真正值得看的是,原生实现正在把 TypeScript 类型检查从构建瓶颈里拽出来。
Bun 快得最猛,但题目没做完
榜单里最扎眼的是 Bun。
在六个真实应用里,bun check 几乎每一项都最快。唯一例外是 tRPC server package,tsc-rs 用 0.09 秒 赢过 bun check 的 0.12 秒。
其他项目,Bun 的速度都很凶。
Excalidraw 快 29 倍。
Playwright 快 25 倍。
TypeORM 快 20 倍。
不过,素材里有一个星号提醒。
bun check 报出了其他检查器都没报的错误:Sentry 3 个,tRPC 2 个。
这就有意思了。
快,当然香。
但如果快出来的结果和别人不一样,工程师就得停下来问一句:这是更严格,还是误报?
T3 Code 的测试里,Bun 的优势也很明显。
不开 Effect 诊断时,bun check 只要 4.07 秒,比 tsc 6 快 15.4 倍,也比 tsc 7 快 3.95 倍。
但问题是,它没有 Effect 诊断。
所以 Effect 项目还得再来一轮。
加上 effect-tsgo diagnostics 后,总时间变成 37.60 秒。
速度还在,但优势被拉回来了。
Effect 一开,分水岭就出来了
如果说真实应用测试拼的是裸速度,T3 Code 的 Effect 测试拼的是完整度。
T3 Code 用了 Effect。素材给了两组结果。
不开 Effect 诊断,Bun 最快。
开了 Effect 诊断,局面变了。
tsc-rs 内置 Effect 诊断,11.13 秒。
tsc 7 加 @effect/tsgo,21.07 秒。
bun check 先跑检查,再补 Effect 诊断,37.60 秒。
tsc 6 加 @effect/language-service,138.63 秒。
这组数据里,最关键的地方是完整度。
tsc-rs 可以一次检查拿到 Effect 诊断。
Bun 需要两步。
对 CI 来说,两步意味着更多流程,更多拼接,更多可能出错的地方。
错误数量也很关键。
素材写得很细:tsc-rs,tsc 7 加 @effect/tsgo,以及 effect-tsgo diagnostics,三者都报告相同的 221 个 Effect diagnostics。
tsc 6 用的是 JavaScript 版 Effect 插件,规则集不同。在 apps/server 上,它报了 287 个,其他检查器报 177 个。
另外,tsc-rs 和 tsc 6 还多报了一个 TS2322 错误。TypeScript 7.1.0-dev 也会报这个错误,对应 PR 已经修复。
老实讲,工程世界最折磨人的往往不是慢。
慢还能摸鱼。
规则不一致才真要命。
速度榜背后,还埋着几颗硬钉子
这份基准并不是把项目拖进来就跑。
测试机是 Apple M4 Pro,12 核,48GB 内存。工具是 hyperfine,5 次运行取中位数,先预热一次,参数包括 --noEmit --incremental false。
tsc 7 和 tsc-rs 以原生二进制运行,不走 npm launcher。
tsc 6 跑在 Node 24.19 上,并且给了 16GB heap。原因也很直接:默认 heap 会在 VS Code 和 Sentry 上内存不够。
这里就有个细节。
老版 JavaScript 编译器不只是慢,还在大项目上吃资源。
版本也很具体:tsc-rs 0.1.0,TypeScript 7.0.2 和 6.0.3,Bun canary bd599f5af。
为了让配置在 tsc 7.0.2 下 0 错误通过,四个应用还做了调整。
Excalidraw 去掉了 baseUrl,因为 TS 7 移除了它。
TypeORM 把 moduleResolution 从 node 改成 nodenext,因为 TS 7 移除了 node。
VS Code 涉及 postinstall 添加的 electron typings。
Playwright 涉及构建生成的源码。
还有两个项目没进表。
rxjs main 需要先构建 workspace packages。
date-fns 使用 project references,tsc -p 和 bun check 在那里做的工作不同。
这些边角料很重要。
因为编译器比拼到最后,demo 跑分只是入场券。
真正难啃的是大型仓库,奇怪配置,历史包袱,插件生态。
tsc-rs 也有自己的锋利处。
它在 VS Code 上报了 10 个错误,在 Sentry 上报了 2 个错误。素材说,TypeScript 7.1.0-dev 也会逐行报出同样错误。原因是 tsc-rs 移植的是 7.1 dev 版本,里面有一些 7.0.2 没有的检查。
评论区也有人盯着实现细节。
I'm curious how much unsafe this uses? Are these ports making idiomatic code or just using unsafe all over.
这个问题不轻。
Rust 重写如果靠大量 unsafe 堆速度,故事就会变味。
目前素材没有给出 readme 说明,所以答案还悬着。
当类型检查从几十秒压到几秒,开发体验会被重新设计。
但最快的跑分,能不能变成最稳的默认选项?
【锐评】:编译器狂飙很爽,但诊断一致性、生态兼容和历史包袱,才是最后真正的硬仗。
参考链接:
https://github.com/pingdotgg/ts-rust