
Creator of TypeScript: 10x Faster Typescript, Why AI Won't Replace SWEs | Anders Hejlsberg
Executive Summary
Ryan Peterman interviews Anders Hejlsberg about TypeScript's origins, the native rewrite of its compiler, programming-language design, and software engineering in the age of AI. Hejlsberg explains that TypeScript began in JavaScript because self-hosting kept the project inside its target ecosystem and enabled broad tooling and platform reach. He describes the native rewrite as a performance and scalability project, with shared-memory concurrency and multi-core utilization as important motivations. The team chose to port the existing compiler to preserve semantics and compatibility rather than start over. The conversation closes with a cautious view of AI-assisted programming: agents can write substantial amounts of code, but engineers still need to understand, review, and take responsibility for the result.
Chapters & Key Takeaways
Why TypeScript Needed a Native Rewrite, and What AI Programming Changes
Why TypeScript Started in JavaScript
Anders Hejlsberg explains that TypeScript began inside the JavaScript ecosystem because self-hosting made the team daily users of the language and its tools. That feedback made failures visible quickly, while JavaScript's reach meant the compiler could run across platforms, including the browser. 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” Direct Audio Anchor Listen from 00:02
The Type Checker Serves Developers
In Hejlsberg's account, TypeScript is unusual because its types do not change runtime behavior. They primarily support developer tools such as completion, refactoring, and code navigation, while the gradual type system allows typed and untyped code to coexist. Anders Hejlsberg 10:39 “the types have no impact whatsoever on the runtime behavior of the code” Direct Audio Anchor Listen from 10:39
Performance and Concurrency First
Hejlsberg presents the native rewrite as a response to performance and scalability. Anders Hejlsberg 00:02 “the problem we were trying to solve it's real simple performance and scalability” Direct Audio Anchor Listen from 00:02 JavaScript imposed a compute penalty for compiler workloads and limited shared-memory concurrency, leaving modern multi-core processors underused.
Choosing Go Under Constraints
The language choice was described as a structured engineering decision. The replacement had to provide native performance and access to shared-memory concurrency while remaining practical for a compiler that had accumulated substantial behavior and compatibility expectations. Anders Hejlsberg 20:40 “the decision process was actually pretty structured” Direct Audio Anchor Listen from 20:40
Compatibility Over Reinvention
The rewrite was approached as a port rather than an opportunity to invent a new TypeScript. Preserving the existing compiler's semantics and behavior mattered because users and tools depend on the language as it already works. Anders Hejlsberg 31:22 “we wanted to preserve the semantics and the behavior of the existing compiler” Direct Audio Anchor Listen from 31:22
AI and Incumbent Languages
Hejlsberg argues that AI is strongest with languages represented heavily in its training data, including JavaScript, TypeScript, and Python. He also says static validation can let an agent find some mistakes before presenting code, though he describes this as an advantage rather than a guarantee. Anders Hejlsberg 41:57 “AI is best at the languages it has seen the most of in its training set” Direct Audio Anchor Listen from 41:57
Human Responsibility Remains
The closing argument is not that AI makes engineering disappear. Hejlsberg says someone still needs to understand what the generated code is doing, how it relates to the business problem, and whether it is relevant to the organization. The tools may change how code is produced, but responsibility for understanding and reviewing the result remains human. Anders Hejlsberg 51:38 “there still has to be someone understanding what's going on” Direct Audio Anchor Listen from 51:38 Continue at 61:33
Interview Highlights9 exchanges
Direct dialogue & timestamps from the recording
Why was the TypeScript compiler originally written in JavaScript?
No, it's a good question. Honestly, if you had told me before the TypeScript project started, "Anders, you're going to be writing compilers in JavaScript," I'd have said no. The original prototypes for TypeScript — the Strata project, as it was called in the very early days — were actually written in C, as an adaptation of the JavaScript parser we had in IE, and then we moved to JavaScript but wrote it sort of C-style. Why did we go with JavaScript? If you can self-host in the ecosystem that you want to be a part of, that is dramatically better than putting yourself outside the ecosystem and trying to target it. By writing it in TypeScript, we were daily users of TypeScript and of the tooling we were building; whenever something didn't work right or didn't perform, we knew it immediately. In the early years that was a huge boon. JavaScript also runs everywhere, so our compiler could automatically run on every platform, even in the browser — a native-code solution at that time couldn't have run in the browser, and WebAssembly didn't exist yet. And people didn't really realize how fast JavaScript had gotten: Google's work with V8 brought it within two to 3x of native code, which is pretty darn impressive. It was fully possible to write performant compilers in JavaScript, because often it's the algorithms that determine how performant you are, not necessarily the runtime environment.
Is there anything unique about the TypeScript compiler?
I think there are a couple of things that are pretty unique about TypeScript's compiler. First of all, it doesn't target machine code — it targets JavaScript; some people call that a transpiler, and in a sense the compilation phase is mostly about removing the type annotations and turning your TypeScript back into JavaScript. In the early days an important part of the compilation process was also to downlevel your JavaScript, because the runtime environments at the time were not evergreen — some lagged multiple years behind the standard. When classes got standardized, most JavaScript runtimes didn't implement them, but it turns out you can downlevel to constructor functions and transform the code. And then of course the type checking is the big thing. The type checker in TypeScript is quite unlike any other type checker in other compilers: typically type checkers exist to guide the code generator, but since we erase the types, the types have no impact whatsoever on the runtime behavior of the code. They purely exist for tooling sake and for the developer sake, to guide things like statement completion, refactoring, and code navigation. TypeScript also has a gradual type system: you can have half of your code typed and the other half is just any, and very few languages have anything like that. That actually made it fascinating to work on, because we were solving problems that no one had solved before.
So with the the native rewrite of this originally fully JavaScript compiler, what was the problem you were trying to solve with the rewrite and why did you end up write rewriting it in native code?
The problem we were trying to solve is real simple: performance and scalability. JavaScript was never really optimized for compute-intensive workloads like compilers — it's more about creating UI that runs in a browser. In JavaScript, first of all, you pay a 2 to 3x performance penalty compared to native code; it depends on the workload, but for compute that's about where it's at. And secondly there are a lot of restrictions around use of concurrency. JavaScript was always engineered to be a single-threaded language — that's why we have callbacks and async, because you can't spin up threads. That's mostly a good thing, because concurrency in a language with mutable data is very hard: you can have races and deadlocks and all of that. Functional programming languages are easier with concurrency because all the data is immutable. JavaScript doesn't give you access to concurrency other than web workers, and web workers can't share data between each other other than by remoting it — one worker has to turn data into JSON to hand it to another. In other words, you can't have shared-memory concurrency, and really that's what we wanted in order to gain the performance we were leaving on the table by not utilizing the multi-core CPUs everyone has today. Moore's law has stopped giving us faster CPUs; it's giving us more CPUs, and we were leaving money on the table in multiple ways. So we looked for a language that would get us out of both of those binds: it had to be native, and it had to have access to shared-memory concurrency.