Creator of TypeScript: 10x Faster Typescript, Why AI Won't Replace SWEs | Anders Hejlsberg

Creator of TypeScript: 10x Faster Typescript, Why AI Won't Replace SWEs | Anders Hejlsberg

主持人
Ryan Peterman
嘉宾
Anders Hejlsberg

核心摘要

Ryan Peterman 采访 Anders Hejlsberg,讨论 TypeScript 的起源、编译器的原生重写、编程语言设计,以及 AI 时代的软件工程。Hejlsberg 解释说,TypeScript 最初使用 JavaScript,是因为自举让项目留在目标生态内部,并获得广泛的工具与平台覆盖。他把原生重写描述为一个性能和可扩展性项目,共享内存并发与多核利用是重要动因。团队选择移植现有编译器,以保留语义和兼容性,而不是从头重写。对话最后对 AI 辅助编程持谨慎态度:智能体可以编写大量代码,但工程师仍必须理解、审核并对结果负责。

章节看点与核心要点

Hejlsberg 把在 JavaScript 生态中自举描述为 TypeScript 早期的一项战略优势。
原生编译器重写被描述为对计算性能、可扩展性和并发约束的回应。
这次重写是一项移植工作,目标是保留现有语义和向后兼容行为。
TypeScript 的类型系统被描述为主要服务于开发者工具,而不是改变运行时行为。
Hejlsberg 不认同 AI 生成代码会消除工程师理解和审核工作的必要性。
访谈把软件工程描述为正在转向监督和审核智能体生成的代码,同时保留人的最终责任。

TypeScript 为什么重写原生编译器,以及这对 AI 编程意味着什么

TypeScript 为什么从 JavaScript 开始

Anders Hejlsberg 解释说,TypeScript 最初留在 JavaScript 生态中,是因为自举让团队成为这门语言及其工具的日常用户。 问题会很快暴露,而 JavaScript 的覆盖范围也让编译器能够跨平台运行,包括浏览器。

类型检查器的目标不是改变运行时

按照 Hejlsberg 的说法,TypeScript 的特殊之处在于类型不会改变代码的运行时行为。 类型主要服务于补全、重构和代码导航等开发工具;渐进式类型系统也允许有类型和无类型的代码共存。

原生重写首先解决性能与并发

Hejlsberg 将原生重写描述为对性能和可扩展性问题的回应。 对于编译器这类计算密集型工作负载,JavaScript 会带来性能损失,也限制了共享内存并发,使现代多核处理器没有被充分利用。

选择 Go 是一次受约束的工程决策

他将语言选择描述为一次结构化的工程决策。 替代实现需要提供原生性能和共享内存并发,同时还要适合一个已经积累了大量行为与兼容性预期的编译器。

兼容性比重新发明语言更重要

这次重写被当作移植,而不是重新发明 TypeScript 的机会。 保留原有编译器的语义和行为很重要,因为用户和工具都依赖这门语言既有的工作方式。

AI 会放大成熟语言的优势

Hejlsberg 认为,AI 最擅长的是训练数据中出现最多的语言,包括 JavaScript、TypeScript 和 Python。 他还表示,静态验证可以让 agent 在提交代码之前发现部分错误,但这是一种优势,并不是保证。

代码可以由 AI 写,但责任不能外包

最后的观点并不是 AI 会让工程消失。 Hejlsberg 说,仍然需要有人理解生成的代码在做什么、它如何对应业务问题,以及它是否与组织相关。 代码的生产方式可能改变,但理解并审查结果的责任仍然在人。

访谈精粹9 组核心对谈

主持人与特邀嘉宾原声对谈精选,支持精准时间戳跳转

QRyan Peterman
1:15

那么,TypeScript 最初为什么要用 JavaScript 编写编译器?

AAnders Hejlsberg

这是个好问题。坦白说,如果在 TypeScript 项目开始前有人对我说“Anders,你要用 JavaScript 写编译器”,我会拒绝。TypeScript 最早的原型——早期叫 Strata 项目——其实是用 C 写的,改编自我们在 IE 里的 JavaScript 解析器,后来才转到 JavaScript,但写法仍偏 C 风格。为什么选 JavaScript?如果你能在自己想融入的生态里自举,那远好过站在生态外面去瞄准它。用 TypeScript 写,我们自己就是 TypeScript 和工具链的日常用户,哪里不对、哪里性能不行,我们立刻就知道,早期这是巨大的红利。而且 JavaScript 无处不在,编译器因此能自动跑在所有平台、甚至浏览器里——当时的原生方案做不到这一点,WebAssembly 那时还不存在。另外当时人们没意识到 JavaScript 已经变快了:Google 用 V8 把它带到原生代码两到三倍性能以内,相当了不起。用 JavaScript 写出高性能编译器完全可行,因为决定性能的往往是算法,而不一定是运行时环境。

QRyan Peterman
3:39

TypeScript 编译器有什么独特之处?

AAnders Hejlsberg

TypeScript 编译器有几处相当独特。首先它不以机器代码为目标,而是以 JavaScript 为目标——有人称之为转译器,某种意义上编译阶段主要就是移除类型注解、把 TypeScript 还原回 JavaScript。早期编译流程还有个重要部分是降级处理:当时的运行环境并非常青,有的落后标准好几年。比如类标准化后多数 JavaScript 运行时并不支持,但可以降级为构造函数并做代码转换。再就是类型检查这个重头戏。TypeScript 的类型检查器与其他编译器里的很不一样:通常类型检查器是为代码生成器服务的,而我们擦除类型,类型对代码的运行时行为毫无影响,纯粹为工具和开发者服务——语句补全、重构、代码导航。TypeScript 还是渐进类型系统:一半代码可以有类型,另一半就是 any,极少有语言具备这种设计。这让这份工作非常迷人,因为我们解决的是前人没解决过的问题。

QRyan Peterman
7:29

对于这个最初完全用 JavaScript 编写的编译器,原生重写试图解决什么问题?为什么最后要用原生代码重写?

AAnders Hejlsberg

我们要解决的问题很简单:性能与可扩展性。JavaScript 从未真正为编译器这类计算密集型负载优化过——它更偏向浏览器里运行的 UI。首先,用 JavaScript 做计算,相比原生代码要付出两到三倍的性能代价,具体取决于负载,但计算密集型大致就是这个水平。其次,它对并发的使用限制很多。JavaScript 一直被设计成单线程语言——这正是回调和 async 存在的原因:你开不了线程。这多半是好事,因为在有可变数据的语言里并发非常难:竞态、死锁这些问题都会出现。函数式编程语言的并发更容易,因为数据都不可变。JavaScript 除 Web Worker 外不提供并发,而 Worker 之间无法共享数据,只能序列化传递——一个 Worker 算好的数据要给另一个用,得先转成 JSON。换句话说,没有共享内存并发,而这正是我们想利用当今人人皆有的多核 CPU 所需要的。摩尔定律不再给我们更快的 CPU,而是给我们更多的 CPU,我们在多个方面都在白白浪费机会。所以我们要找一门能同时解开这两个结的语言:它必须是原生的,且必须能访问共享内存并发。