swc 作者为何选择 Go 语言来实现 TypeScript 的类型检查器?

关注者
132
被浏览
108,470
登录后你可以
不限量看优质回答私信答主深度交流精彩内容一键收藏

本文是对作者原文的翻译,原文地址: I’m porting tsc to Go

大家好,我是 SWC 的作者,目前正在将 TypeScript 的类型检查器 tsc 移植到 Go 语言上。众所周知,SWC 是用 Rust 语言实现,而我也是 Rust 的忠实粉丝,为何会有这种奇怪的选择呢?

为何移植 tsc?

随着 TypeScript 在全世界范围内的广泛采用,大型的 TS 项目都在面临一个困境:类型检查已经成为他们工作流程中最慢的环节之一,用静态类型交换开发效率显然不是开发者所希望看到的。

这背后的元凶就是 tsc,它会对类型进行全面的检查,然后将 TS 的代码编译为 Javascript。随着代码规模的快速增长,编译时间也会随之线性增加。对于中大型的项目而言,这个编译时间可能会变得非常慢,好在,有了 SwC,从 TS 到 JS 的转译时间被大大缩短了,但是类型检查依然是瓶颈之一。

什么是类型检查器?

类型检查器会在你的程序执行前对其进行正确性检查,这样可以确保在函数调用或变量赋值时使用了正确的值。同时为了避免开发者需要在所有地方都手动声明类型,tsc 同时还提供了强大的类型推导功能。

总之,类型检查可以帮助我们实现更可靠的代码,阻止可能的错误,特别是在代码重构时,类型检查更是可以发挥巨大的作用(重构过中大型动态语言项目的同学相信都深有感悟)。

可能读者大大们已经有些着急了,我是冲着你的题目来的,内容呢?别急,本文的重头戏来了。

为何不使用 Rust

首先,我并不是一开始就决定使用 GO 语言,而是试图用 Rust 去重写 tsc,简而言之,这次重写非常愉快也非常有趣。

同时,Rust 版本的重写相比原来的 tsc 提升了 62 倍的性能!为了测量编译时间的提升,我使用了 conformance 测试工具,该工具是由 TypeScript 官方编译器提供的,下面是时间对比:

  • tsc: 133.2秒
  • Rust 重写版: 2.13秒!

既然提升这么大,为何最终却放弃了?

这里其实就涉及到重写和移植这两者的区别了。tsc 不可否认是一个非常大的项目,因此重写会是一件非常非常艰巨的事,但是这里的性能提升却非常诱人,而且我对于 Rust 的喜爱也加分不少。好在,还可以选择使用 Rust 移植,相比起重写,移植会轻松愉快的多!

但是,在深入阅读过 TypeScript 的源码后,我发现了几点至关重要的问题:

  • tsc 使用了大量共享可变性,例如两个可变引用指向同一个数据,并且很多实现非常依赖于垃圾回收(GC)
  • tsc 使用了大量的自引用结构,熟悉 Rust 的同学都知道,这种数据结构在 Rust 中有多么难

如果是重写,上面的都没有问题,可以重新选择其它解决方案实现,但是既然选择了移植,那么上面的问题就成了绕不过去的坎。

虽然我是 Rust 的拥护者和信徒,但是经过深思熟虑后,依然觉得 Rust 在这种场景下,并不是一个好的移植选择。如果使用 Rust,就必须大量依赖于 unsafe 的代码实现。

总之,语言的选择更多还是取决于任务的类型而不是喜好,虽然心有不甘,我最终还是选择了 Go 语言(实际还在 Go语言和 Zig 语言间做了一番调研)。

会开源吗?

目前该项目由 Vercel 赞助开发,我们会在未来某一个时间点将其开源,同时还会将 tscswc 桥接起来。

当支持了类型检查后, swc 就可以进一步提升大家的 JS/TS 工具链性能:

  • ✅ 转移 (替换 Babel)
  • 类型检查Type Checking (替换 tsc)
  • 体积缩小 (替换 Terser)
  • 打包 (替换 webpack)