TypeScript 7 正式发布:速度快得让人吃惊

BBetter Stack
Computing/SoftwareInternet Technology

Transcript

00:00:00TypeScript 7 正式发布了,经过一年多的艰苦开发
00:00:05将编译器移植到 Go 和 Bun 证明了你完全可以用 AI 来搞定这一切,所以
00:00:10我相信开发者们对此一定很高兴。如果你现在运行 npm install typescript,你就会得到
00:00:16第 7 版,也就是带有 Go 编译器的版本。紧接着微软几天前发布的
00:00:20公告视频,我想深入探讨一下他们究竟是如何实现如此巨大的性能
00:00:25提升的。其结果令人印象深刻。新编译器构建了 VS Code 代码库
00:00:31——跨越近 8,000 个文件、共 130 万行代码,仅需 10 秒,而旧编译器则需要 125
00:00:39秒。随着 TypeScript 现在稳坐 GitHub 语言榜首
00:00:44信不信由你,这个新编译器将改变许多人的生活。想到这里我都有些激动了
00:00:49不过与其去比较 6 和 7 之间几乎相同的特性,我更想深入研究
00:00:54这种性能到底是如何实现的。所以让我们来看看新的编译器,看看
00:00:59是什么让它变得这么快。那么为什么要重写呢?C# 的创造者兼微软技术院士
00:01:09Anders Hejlsberg 说得很直接:JavaScript 是为 UI 和浏览器优化的,它并不是
00:01:16针对计算密集型工作流和编译器优化的。这很明显,因为它是单线程的
00:01:22处理像抽象语法树或类型检查这样的工作,单核的性能扩展极其有限
00:01:27技术上你可以把工作分发给不同的 worker,但那样的话你就
00:01:32需要对数据进行序列化和反序列化,这既慢又耗内存,而且你还得
00:01:38应对管理这一切的复杂性。因此他们选择了 Go,通过使用一种完全符合该需求的语言
00:01:43带来了巨大的性能提升。Go 是一种编译型语言
00:01:48这意味着我们可以直接在 CPU 上运行编译后的代码,而不需要解释执行;而且 Go 拥有
00:01:54非常出色的并发模型,这意味着我们可以轻松地同时运行多个执行线程
00:02:00并且它们共享内存。这意味着我们可以充分利用机器上的所有核心,而不仅仅是
00:02:06其中一个。这种提速大致是平分的,多达一半的加速归功于原生代码
00:02:12其余则来自于共享内存并发。Go 基本上是此场景的完美语言
00:02:17这不仅是因为 Go 更快,更是因为它字面上有更多的资源来解决这个问题,而结果
00:02:23胜于雄辩。看看数据,拥有 130 万行代码的 VS Code,旧编译器耗时 125.7 秒,新编译器
00:02:32只需 10.6 秒,提升了 11.9 倍;对于 Bluesky,从 24.3 秒缩短到 2.8 秒,提升了 8.7 倍
00:02:42接着是 Playwright,从 12.8 秒降至 1.5 秒,同样是 8.7 倍的提升。有趣的是,订阅
00:02:49Better Stack 只需要 1 秒。这里的差距只会越来越大,因为核心的单核速度增长
00:02:54已经不如从前了,但我们拥有的是更多的核心。比如我用的是 M3 Max
00:03:00它拥有 14 个核心,这太狂暴了。我记得大学时组装过一台游戏电脑
00:03:06里面用的是四核 Intel i7,当时觉得简直是神机。JavaScript 编译器只能
00:03:11用到其中的一个核心,但有了 Go,我可以解锁所有核心。当然,核心越多,你能获得的
00:03:17性能就越高。如果我在这台机器上用第 6 版编译一个大型项目,你会看到花了 45
00:03:24秒,而用第 7 版,我们可以把时间缩短到仅需 3 秒。编译过程
00:03:29包含多个阶段,类型检查阶段只是其中之一。针对这一点,Go
00:03:34会启动 4 个类型检查器,让他们各自检查四分之一的代码库。但你其实
00:03:39还可以进一步优化。在编译时,你可以将检查器数量设置为 12,以获得更快的性能
00:03:45我已经把 VS Code 仓库下载到了本机,这样我们就能看到 TypeScript 7 和 6 之间的差别
00:03:51默认情况下,我机器上的 tsc 现在指向 TypeScript 7,我们直接对它运行
00:03:57诊断命令,你可以看到花了 5.4 秒,而大部分时间实际上花在了
00:04:02检查时间上(占了 4.7 秒)。我们其实还可以进一步加速。如果我们加上另一个标志
00:04:08checkers 12,可以看到总时间从 5.4 秒降到了 3.5 秒,所有的省时
00:04:15都来自于检查时间的减少。这里原本是 4.7 秒,现在降到了 2.9 秒。现在让我们运行相同的
00:04:20命令,但这次使用 TypeScript 6,这需要一点
00:04:23时间,所以我们快进一下。等待之后,你可以看到 TypeScript 6 最终耗时
00:04:2845.3 秒,而利用所有核心运行时的耗时仅为 3.5 秒。这相当于
00:04:36在我这台 M3 Max 芯片上实现了 15 倍的提升。对我来说,这 3.5 秒的成果就是这么来的。这样做
00:04:43当然会占用其他进程的性能,但如果你当时没在做其他事
00:04:47不妨用尽机器的所有资源。让我们来看看新编译器里的真实代码
00:04:52来理解 Go 是如何实现这种性能的。这里有一个 bind source files 函数
00:04:57它的工作是构建文件中的每一个声明,并分析出它属于哪个作用域
00:05:03我们可能要处理数千个文件。我们遍历每个文件,把绑定任务排入队列
00:05:09然后等待全部完成。最精妙的部分在于,我们根本不用去思考这些工作
00:05:14是如何分发给各个核心的,Go 的运行时会搞定这一切。我们不需要手动创建线程,或者
00:05:20管理核心之间复杂的通信,我们只需要交代“这是要做的任务”,它自己就会找出完成它的方法
00:05:26在 JavaScript 中,我们可以写出几乎相同的代码,但对于 CPU 密集型任务来说毫无意义
00:05:32它依然只会在单个线程上顺序执行,哪怕 Promise 给人一种平行的假象
00:05:38Worker 线程在技术上是一种绕过方案,但你无法在它们之间共享对象,只能通过 SharedArrayBuffer
00:05:43传递原始字节。因此,将解析后的语法树交给 Worker 意味着要将整个树序列化
00:05:48复制过去,并在另一端重新构建。对于大文件来说,这开销可能比计算本身还大
00:05:53本质上,正是这类特性让 Go 在处理 CPU 密集型任务时天然更胜一筹。它拥有
00:05:58能够为你处理所有这些并发的运行时,而且它拥有共享内存,你可以直接传递对象
00:06:03而无需进行复制。除了编译时间外,你感触最深的
00:06:07莫过于语言服务器的速度了。任何在大型 TypeScript 代码库上工作过的人都知道
00:06:13等待类型检查是多么痛苦,天哪,那些 Intel Mac 用户对此体会尤深。我记得几年前
00:06:20在项目中工作时,你打开仓库,文字下面出现波浪线提示
00:06:24字面意义上要等上整整两分钟。而且每当你做了一点修改
00:06:29过程都极其痛苦。开发者体验非常糟糕,之后就没人愿意
00:06:34维护那个代码库了。然而,新的语言服务器意味着在 IDE 中能获得实时反馈,你可以
00:06:40打开文件、做出修改,并在几毫秒内看到错误提示,哪怕你处理的是
00:06:46庞大无比的代码库。除了性能之外,新的语言服务器现在更加稳定,因此
00:06:51当类型检查失灵时需要重启 IDE 的情况大幅减少。TypeScript 7
00:06:57将语言服务器命令失败率降低了 80% 以上,并将服务器崩溃率降低了 60%
00:07:04以上。这意味着摔笔记本电脑的人少了,这实际上对环境保护挺有好处。
00:07:08谁能想到微软居然还关心地球呢?另外值得一提的是,这是一个移植版本
00:07:13而非全新重写。TypeScript 团队非常谨慎,以确保新编译器基本上
00:07:19与旧版本完全兼容。除了速度变快,你可能甚至感觉不到差异。但是
00:07:24虽然你现在可以在自己的项目上运行 TypeScript 7,但你仍然需要等待你最喜欢的
00:07:29软件包跟上进度。它们的编程式 API 仍然缺失,这意味着任何依赖于
00:07:35该 API 的包都必须等待 7.1 版本的发布。像 typescript-eslint、ts-jest 或 ts-node 这类包
00:07:42进度会落后一些。现在 TypeScript 7 的完整发布版已提供下载,但你确实
00:07:47需要显式安装 VS Code 的 TypeScript 7 扩展。默认包最终会被
00:07:53更新,但眼下直接安装那个扩展即可。它在扩展商店里叫 TypeScript 7,之后
00:07:58一切就会如期运行。如果你想了解更多关于 TypeScript 7 的功能特性,我们
00:08:03专门针对它拍了一期视频,你可以在这里观看。如果你喜欢这样的硬核解析,请订阅
00:08:08Better Stack 获取更多内容。希望你从中学到了一些东西,现在你可以享受到
00:08:12TypeScript 7 带来的飞速开发体验。我敢肯定我非常享受
00:08:16使用它。感谢收看,我们下期视频再见!

Key Takeaway

TypeScript 7 通过使用 Go 语言改写编译器并利用共享内存并发,使 VS Code 代码库的编译速度提升近 12 倍,同时将语言服务器崩溃率降低了 60% 以上。

Highlights

  • TypeScript 7 将编译器从 JavaScript 移植到 Go,构建拥有 130 万行代码的 VS Code 只需 10 秒,比旧版的 125 秒提升近 12 倍。

  • JavaScript 的单线程机制无法有效利用多核 CPU,而 Go 拥有共享内存并发模型,能够解锁多核硬件性能。

  • 通过设置 checkers 标志将类型检查器数量调整为 12,在 M3 Max 芯片上可将 VS Code 的检查时间进一步降至 2.9 秒,总体性能提升 15 倍。

  • 新语言服务器使命令失败率降低 80% 以上,服务器崩溃率降低 60% 以上。

  • TypeScript 7 暂未提供编程式 API,依赖该 API 的工具(如 typescript-eslint、ts-jest、ts-node)需等待 7.1 版本。

Timeline

TypeScript 7 发布与编译性能突破

  • TypeScript 7 借助 Go 和 Bun 将编译器进行了重构移植。
  • 跨越近 8,000 个文件、130 万行代码的 VS Code 代码库构建时间从 125 秒缩短至 10 秒。

运行 npm install typescript 即可获取带有 Go 编译器的第 7 版。性能大幅提升的主要动力来自底层的语言重构。新编译器在处理大型项目时展现出数量级的速度提升。

放弃 JavaScript 并转向 Go 的底层原因

  • JavaScript 为 UI 和浏览器设计,其单线程特性限制了 CPU 密集型任务的扩展。
  • Go 语言提供编译型原生代码与共享内存并发模型,避免了跨 Worker 序列化数据的开销。
  • 在不同大型项目上,新编译器均展现出 8 到 12 倍不等的性能提升。

JavaScript 在处理抽象语法树或类型检查时仅能使用单个 CPU 核心。即便使用 Worker 线程,传输数据也需要序列化和反序列化,带来高额的内存与计算开销。Go 允许多个线程直接共享内存并利用多核并行处理。Bluesky 的编译时间从 24.3 秒缩短至 2.8 秒(8.7 倍),Playwright 从 12.8 秒降至 1.5 秒(8.7 倍)。

多核并行类型检查的实际效果

  • Go 编译器默认启动 4 个类型检查器并行处理代码库。
  • 通过手动指定 checkers 12 参数,可将 VS Code 的诊断检查时间从 4.7 秒降至 2.9 秒。
  • 在拥有 14 核的 M3 Max 芯片上,TypeScript 7 比 TypeScript 6 快 15 倍。

TypeScript 6 在 M3 Max 机器上执行诊断命令需要 45.3 秒。TypeScript 7 默认只需 5.4 秒,在使用 checkers 12 标志充分利用所有核心后,总时间降至 3.5 秒。只要系统有空闲资源,多核心并行能成倍减少等待时间。

Go 运行时如何简化并发与内存共享

  • Go 运行时会自动分发绑定任务到各个核心,无需手动管理线程通信。
  • 共享内存机制允许在线程间直接传递语法树对象,省去了复制开销。

在 bind source files 函数处理数千个文件时,Go 运行时会自动分配并行任务。相比之下,JavaScript 的 Worker 机制必须将解析后的语法树序列化为原始字节传输,其开销往往超过计算本身。Go 凭籍内存共享和原生并发处理计算密集型任务效率更高。

IDE 实时反馈提升与生态兼容现状

  • 新语言服务器实现毫秒级错误提示,并将崩溃率降低 60% 以上。
  • 缺少编程式 API 导致 typescript-eslint 等工具需等待 7.1 版本。
  • 在 IDE 中开启新特性需要显式安装 VS Code 的 TypeScript 7 扩展。

旧版 TypeScript 在大型仓库中生成错误提示可能需要数分钟,而新版语言服务器能提供近乎实时的 IDE 反馈。服务器命令失败率降低了 80% 以上。因为 TypeScript 7 属于移植而非重写,总体兼容性良好,但部分依赖编程式 API 的第三方工具进度会相对滞后。

Community Posts

View all posts