TypeScript 为什么重写原生编译器,以及这对 AI 编程意味着什么
TypeScript 为什么从 JavaScript 开始
Anders Hejlsberg 解释说,TypeScript 最初留在 JavaScript 生态中,是因为自举让团队成为这门语言及其工具的日常用户。 Anders Hejlsberg 00:02 “if you can self-host in the ecosystem that you want to be a part of, then that is just dramatically better” 原声音频直达锚点 播放原声 00:02 问题会很快暴露,而 JavaScript 的覆盖范围也让编译器能够跨平台运行,包括浏览器。
类型检查器的目标不是改变运行时
按照 Hejlsberg 的说法,TypeScript 的特殊之处在于类型不会改变代码的运行时行为。 Anders Hejlsberg 10:39 “the types have no impact whatsoever on the runtime behavior of the code” 原声音频直达锚点 播放原声 10:39 类型主要服务于补全、重构和代码导航等开发工具;渐进式类型系统也允许有类型和无类型的代码共存。
原生重写首先解决性能与并发
Hejlsberg 将原生重写描述为对性能和可扩展性问题的回应。 Anders Hejlsberg 00:02 “the problem we were trying to solve it's real simple performance and scalability” 原声音频直达锚点 播放原声 00:02 对于编译器这类计算密集型工作负载,JavaScript 会带来性能损失,也限制了共享内存并发,使现代多核处理器没有被充分利用。
选择 Go 是一次受约束的工程决策
他将语言选择描述为一次结构化的工程决策。 Anders Hejlsberg 20:40 “the decision process was actually pretty structured” 原声音频直达锚点 播放原声 20:40 替代实现需要提供原生性能和共享内存并发,同时还要适合一个已经积累了大量行为与兼容性预期的编译器。
兼容性比重新发明语言更重要
这次重写被当作移植,而不是重新发明 TypeScript 的机会。 Anders Hejlsberg 31:22 “we wanted to preserve the semantics and the behavior of the existing compiler” 原声音频直达锚点 播放原声 31:22 保留原有编译器的语义和行为很重要,因为用户和工具都依赖这门语言既有的工作方式。
AI 会放大成熟语言的优势
Hejlsberg 认为,AI 最擅长的是训练数据中出现最多的语言,包括 JavaScript、TypeScript 和 Python。 Anders Hejlsberg 41:57 “AI is best at the languages it has seen the most of in its training set” 原声音频直达锚点 播放原声 41:57 他还表示,静态验证可以让 agent 在提交代码之前发现部分错误,但这是一种优势,并不是保证。
代码可以由 AI 写,但责任不能外包
最后的观点并不是 AI 会让工程消失。 Hejlsberg 说,仍然需要有人理解生成的代码在做什么、它如何对应业务问题,以及它是否与组织相关。 Anders Hejlsberg 51:38 “there still has to be someone understanding what's going on” 原声音频直达锚点 播放原声 51:38 代码的生产方式可能改变,但理解并审查结果的责任仍然在人。
访谈精粹9 组核心对谈
主持人与特邀嘉宾原声对谈精选,支持精准时间戳跳转
那么,TypeScript 最初为什么要用 JavaScript 编写编译器?
这是个好问题。坦白说,如果在 TypeScript 项目开始前有人对我说“Anders,你要用 JavaScript 写编译器”,我会拒绝。TypeScript 最早的原型——早期叫 Strata 项目——其实是用 C 写的,改编自我们在 IE 里的 JavaScript 解析器,后来才转到 JavaScript,但写法仍偏 C 风格。为什么选 JavaScript?如果你能在自己想融入的生态里自举,那远好过站在生态外面去瞄准它。用 TypeScript 写,我们自己就是 TypeScript 和工具链的日常用户,哪里不对、哪里性能不行,我们立刻就知道,早期这是巨大的红利。而且 JavaScript 无处不在,编译器因此能自动跑在所有平台、甚至浏览器里——当时的原生方案做不到这一点,WebAssembly 那时还不存在。另外当时人们没意识到 JavaScript 已经变快了:Google 用 V8 把它带到原生代码两到三倍性能以内,相当了不起。用 JavaScript 写出高性能编译器完全可行,因为决定性能的往往是算法,而不一定是运行时环境。
TypeScript 编译器有什么独特之处?
TypeScript 编译器有几处相当独特。首先它不以机器代码为目标,而是以 JavaScript 为目标——有人称之为转译器,某种意义上编译阶段主要就是移除类型注解、把 TypeScript 还原回 JavaScript。早期编译流程还有个重要部分是降级处理:当时的运行环境并非常青,有的落后标准好几年。比如类标准化后多数 JavaScript 运行时并不支持,但可以降级为构造函数并做代码转换。再就是类型检查这个重头戏。TypeScript 的类型检查器与其他编译器里的很不一样:通常类型检查器是为代码生成器服务的,而我们擦除类型,类型对代码的运行时行为毫无影响,纯粹为工具和开发者服务——语句补全、重构、代码导航。TypeScript 还是渐进类型系统:一半代码可以有类型,另一半就是 any,极少有语言具备这种设计。这让这份工作非常迷人,因为我们解决的是前人没解决过的问题。
对于这个最初完全用 JavaScript 编写的编译器,原生重写试图解决什么问题?为什么最后要用原生代码重写?
我们要解决的问题很简单:性能与可扩展性。JavaScript 从未真正为编译器这类计算密集型负载优化过——它更偏向浏览器里运行的 UI。首先,用 JavaScript 做计算,相比原生代码要付出两到三倍的性能代价,具体取决于负载,但计算密集型大致就是这个水平。其次,它对并发的使用限制很多。JavaScript 一直被设计成单线程语言——这正是回调和 async 存在的原因:你开不了线程。这多半是好事,因为在有可变数据的语言里并发非常难:竞态、死锁这些问题都会出现。函数式编程语言的并发更容易,因为数据都不可变。JavaScript 除 Web Worker 外不提供并发,而 Worker 之间无法共享数据,只能序列化传递——一个 Worker 算好的数据要给另一个用,得先转成 JSON。换句话说,没有共享内存并发,而这正是我们想利用当今人人皆有的多核 CPU 所需要的。摩尔定律不再给我们更快的 CPU,而是给我们更多的 CPU,我们在多个方面都在白白浪费机会。所以我们要找一门能同时解开这两个结的语言:它必须是原生的,且必须能访问共享内存并发。