将 Go 的 `defer` 添加到用 Go 编写的 TypeScript 编译器:一个教学补丁

开发与编程 yesterday加入收藏

将 Go 的 `defer` 添加到用 Go 编写的 TypeScript 编译器:一个教学补丁
插图 : Momiji Shirogane

Andrew Healey 将 Go 代码的核心关键字 `defer` 直接注入到用 Go 编写的 TypeScript 编译器中。这是理解 parser、checker 和 emitter 的经典案例。

出发点

自从微软宣布将 TypeScript 编译器用 Go 重写(代号“TypeScript Native”项目,目标是让 tsc 速度提升约 10 倍),编译器的源代码已成为一个非常棒的实验场。Andrew Healey 在其博客上发表了一篇非常有启发性的文章,在他的分支中为 TypeScript 添加了关键字 defer——Go 语言的核心语法糖之一。

给那些主要来自 JS 技术栈的同学提个醒:在 Go 中,defer 会安排一个函数在包含它的函数返回时执行。这相当于没有样板代码的 try { … } finally { … },清理调用就直接写在导致其必要的调用旁边:

f, _ := os.Open("file.txt")
defer f.Close() // 会在函数退出时执行
// … 剩余代码

这种模式被大量用于释放资源(文件、互斥锁、连接),而不会陷入嵌套陷阱。

文章拆解

这篇文章的意义并不是“Go 的 defer 明天就会进入 TypeScript”(剧透:不会)。而是一步步跟踪,当你为现代编译器添加一个关键字时,它会做什么。Healey 经历了以下步骤:

  1. 扫描器(词法分析器)——将 defer 识别为专用 token,而非普通标识符。
  2. 解析器——决定 defer 在语法中允许出现的位置(在语句级别可以,在表达式中不行)。
  3. 类型检查器——验证被 defer 的内容确实是一个函数调用。
  4. 发射器(代码生成器)——将 defer 翻译成 JavaScript,在他的实现中,这相当于将包含函数用 try/finally 包裹,并将 defer 的回调推入一个在退栈时执行的数组。

最后一步最有教益:Go 中的 defer 是一种真正的语言机制(由运行时实现),而在 JS 中我们没有类似的原语——因此必须通过 反糖(desugaring)来模拟,这也带来了性能、异常、await 等方面的权衡。

为什么这对我们有意义

由于 TypeScript 的 Go 版编译器是开源的,这类练习正是我们该读的内容,以便:

  • 理解真正的现代编译器架构(而非大学玩具项目)。
  • 看到当目标语言(JS)缺乏所需原语时,翻译时的选择受到哪些限制。
  • 熟悉 AST → 类型检查 → 代码生成的流程,在一个我们已熟悉其表面(tsc)的项目上。

我们顺便注意到,Healey 的补丁是以演示形式提供的,而非上游 PR——TC39 从未将 defer 纳入 JavaScript 标准,微软也没有在 TypeScript 中推动此类专有扩展。

记住:tsc 的 Go 重写的真正收益不仅仅是速度——更是让一个非高级 TypeScript 专家也能读懂的编译器代码。这类教学补丁会越来越多,这是一所绝佳的学校。

Resources

本文由人工智能撰写,并经人工编辑审核。

我们的编辑部
这篇文章对您有帮助吗?

7 人赞了这篇文章

K
Kaito KuroganeSenior Dev Writer
Senior polyvalent developer, backend Go + frontend TS, open source contributor.
分享:
LIVERadio Geek Kitsune
Tap to listen, the same sound for everyone
0··
// Schedule
// all stations
// share a track →
主题
浏览
信息