【由DeepSeek辅助翻译】
原文链接:https://blog.logrocket.com/typescript-go-pragmatic-choice/
发布时间:2025年4月16日 14:51:38(UTC时间)
关于这次移植的技术细节已有大量报道,本文不再赘述。这里呈现的是TypeScript社区两位成员的思考:
- John Reilly是软件工程师,TypeScript早期采用者。他参与维护Definitely Typed——这个高质量类型定义库实现了TypeScript与JavaScript的集成。John撰写了Definitely Typed发展史,并出现在TypeScript纪录片中。他还开发维护了
ts-loader
这个webpack的TypeScript加载器。目前他在南非Investec银行伦敦分部工作。在他看来,伦敦是地球上最伟大的城市 - Ashley Claymore是软件工程师,居住地距John不远,常与他晨间散步讨论TypeScript。他从TypeScript 1.8版本开始使用,深度参与了语言演进。曾为TypeScript贡献代码,现就职于彭博社JavaScript基础设施与工具团队。文中观点仅代表个人
本文将自由呈现我们的反应与期待。准备好迎接观点、思考和感受的碰撞吧。
移植是否必要?
难道之前不够好吗?是,但也不尽然。
近年来JavaScript/TypeScript生态中,越来越多支持JS开发的工具改用其他语言编写:esbuild(Go)、SWC(Rust)、Bun(Zig)、Deno(Rust)。这些工具都带来了显著的性能提升,而TypeScript始终用自身编写。虽然团队持续优化性能,但改进始终是渐进式的。
社区开始涌现自行实现TypeScript加速的尝试。最著名的是SWC作者DongYoon Kang,他先实现了TypeScript转译功能,又尝试构建类型检查器——最初用Rust,后改用Go,最终回归Rust。虽然项目未成功,但这些尝试印证了市场对性能的强烈需求。移植已成必然——若非官方出手,整个生态将陷入困境。而现在,我们迎来了Go版的TypeScript。
性能变革
Go移植对TypeScript意味着什么?根据Josh Goldberg的框架,TypeScript包含四个维度:
- 语言规范
- 类型检查器
- 编译器
- 语言服务
语言规范不受移植影响,语法保持不变。您仍可照常使用type
和interface
。类型检查规则也维持原样,原有错误提示依然有效:
const i: number = "非数字值";
// ts报错:类型'string'不能赋值给类型'number'
真正的变化始于类型检查器、编译器和语言服务——它们将获得数量级的提速。
谁不关心性能?显然没人。当工具卡顿打断工作流时,这种体验令人难以忽视。TypeScript团队始终重视性能,特别是开发工具响应速度。联合创始人Anders Hejlsberg多次强调语言服务器必须提供毫秒级反馈。
这将如何影响生态?简而言之:更快的VS Code和构建流程。
以John所在的Investec银行为例,众多使用VS Code的工程师将获得更流畅的开发体验:项目加载时语言服务启动更快、重构响应更迅捷、”红色波浪线”出现更及时。构建过程同样受益——无论是本地还是持续集成环境,TypeScript编译都将显著加速。这种提升将惠及全球所有TypeScript开发者。
团队将减少TypeScript编写
许多语言采用自举编译器(编译器用自身语言编写),TypeScript自开源以来一直如此。这一传统即将改变——编译器将改用Go编写。这可能是首例为性能主动放弃自举的案例。
对John而言,这是最令人忐忑的变革。TypeScript团队日常将减少TypeScript编写(当然仍会使用)。这可能导致”自食其果“程度降低——语言构建者直接使用体验的反馈减少。
但考虑到活跃的社区生态,这种担忧或许过虑。团队与社区联系紧密,即使自身使用减少,仍有大量开发者提供反馈。值得注意的是,团队内部使用方式本就与主流存在差异——例如长期依赖类(后文详述),直到Jake Bailey主导模块化迁移前仍使用命名空间。这并不妨碍他们对现代JavaScript特性的支持。
另一个潜在担忧是团队对TC39(JavaScript标准委员会)的参与度。TypeScript团队多年来深度塑造了JavaScript发展(如可选链、装饰器等特性)。随着转向Go,有人担心其影响力可能减弱。
作为彭博社TC39代表,Ashley并不担忧。TypeScript产品经理Daniel Rosenwasser刚当选TC39联合主席,团队成员Ron Buckton仍在推动显式资源管理等提案。无论编译器使用何种语言,团队的影响力依然稳固。
交互方式变化
与TypeScript交互的四种主要途径:
- 命令行接口
tsc
- JavaScript API(
import ts from "typescript"
) - 语言服务器
tsserver
- 内部集成TypeScript的工具
这些方式将如何演变?
命令行接口tsc
CLI仍将存在,目标是保持高度兼容。虽然底层改用Go实现,但用户交互方式不变。
JavaScript API
这部分仍在开发中。API将继续存在,但必然有所调整。关键问题是当前同步API是否会因Go调用变为异步——这种迁移通常很困难。好消息是团队计划保持同步API。
语言服务器tsserver
虽然TypeScript启发了LSP规范,但当前实现并不完全兼容。团队将借此次移植实现规范对齐。
集成工具
工具开发者需要适配新版本,但最终用户可能感知不到变化。
生态工具影响
深入探讨内部使用TypeScript的工具,影响确实存在。
John维护的ts-loader
(webpack加载器)依赖多年未变的TypeScript API。他在三月的Bluesky发帖中还称赞其稳定性:
结果TypeScript团队很快宣布了重大变革。根据团队成员Daniel Rosenwasser的说明:
虽然我们会移植大部分现有编译器功能,但并不意味着所有API都会保留
ts-loader
有两种模式:
- 带类型检查
- 仅转译
TypeScript 7很可能需要重大重构才能支持类型检查模式,但仅转译模式(依赖transpileModule
API)可能只需微小调整。这也可能意味着ts-loader
类型检查模式的终结。
Ashley开发的ts-blank-space
(彭博社开源的免source map转换工具)同样依赖TypeScript API。虽然影响尚不明确,但这次变革可能带来新机遇。该工具常被建议改用其他解析器——因为它虽然只使用TypeScript的解析API,却需要导入整个类型检查器。对于已依赖TypeScript的项目没有额外开销,但对其他场景不够友好。
部分工具天然具备过渡路径。例如typescript-eslint
将顺利支持TypeScript 7,团队还计划用新API加速类型化linting。这意味着ESLint用户将自动获得性能提升。
但依赖内部API的工具可能面临重大重构甚至淘汰。这种变化是常态,也完全可以接受。
Go是否明智之选?
当TypeScript不再用自身编写时,社区对替代语言争论激烈。C#支持者希望选择这门Anders参与设计的语言,Rust爱好者则力荐Rust。对后者仍有机会——TypeScript 7的Node.js绑定可能使用Rust包。
John坦言若事先预测,他会猜Rust或Zig(Bun所用语言)。Go看似冷门,但细想却合情合理:esbuild用Go实现已有成功先例;Go具备垃圾回收(Rust没有),大幅简化了代码移植;而C#重度依赖类,与TypeScript编译器轻量使用类的模式不符。
选择Go体现了TypeScript一贯的务实精神。查看其官方设计目标,你会发现”健全性”被明确列为”非目标”——团队始终在正确性与生产力间寻求平衡。简言之:务实是TypeScript的哲学,而Go正是务实之选。
这是否证明JavaScript性能低下?
这只能证明JavaScript可能不是实现类型检查器的最佳语言。引用Anders在《为何选择Go?》中的观点:
没有哪种语言能完美适合所有场景
类型检查是密集型任务——本质上需要逐行模拟程序执行并检测规则违反。程序规模越大,工作量越惊人。
当类型检查器用动态语言编写时,需要额外运行时支持。TypeScript的情况相当于:JavaScript引擎运行着类型检查器,而检查器又在模拟另一个程序。原生运行获得性能提升不足为奇。
改用Go预计带来10倍加速:其中约3.5倍来自原生运行,其余来自更好的并行化。考虑到动态语言与预编译二进制的执行效率差距,这个倍数反而证明了V8引擎的卓越优化。
JavaScript目前可通过SharedArrayBuffer
等底层API实现并行,但需要直接操作字节。Stage 2的共享结构体提案若获通过,将简化多核利用。
当然,JavaScript仍有独特优势:
- 高级特性使程序更精简
- 动态组合逻辑适合插件生态
- 运行时修改代码利于UI开发
- 平台无关性
结语
生态需要更快的TypeScript,性能已成为不可忽视的要素。某种形式的移植势在必行。接受这个前提后,TypeScript团队的迁移方案令人充满信心——他们才华横溢、务实明智。
这次转型必将成功
致谢
感谢TypeScript团队的Jake Bailey审阅本文,以及Josh Goldberg对TypeScript构成的精妙分类。