A Closer Look at the Details Behind the Go Port of the TypeScript Compiler
Background Introduction
Currently, there is no tool that can fully replace the comprehensive type checking capabilities provided by tsc
Microsoft announced today:
[...] We have begun a native port of the
TypeScript
compiler and tools. This native implementation will significantly improve editor startup time, reduce most build times by approximately 10x, and significantly lower memory usage.
This article will delve into the technical details behind this major announcement.
Codebases: JavaScript vs. Native Implementation
To avoid confusion, I will use the following terms:
JavaScript
codebase: The currentTypeScript
codebase — actually written inTypeScript
.Native
codebase: The new codebase. I use the term "native" because that's what theTypeScript
team calls it - actually written inGo
.
Why This is a Major Breakthrough
Type checking is the only task that external tools cannot accomplish:
- Generating
.js
files has become faster — thanks to native tools and type stripping. - Generating
.d.ts
files has become faster — thanks to native tools and isolated declarations.
Therefore, making type checking faster as well is a significant advancement.
Project Timeline
The current TypeScript
version is 5.8.
TypeScript 6
(JavaScript
): The JavaScript
codebase will continue to be used for the 6.x series, with version 6.0 introducing some breaking changes to align with the native codebase.
TypeScript
original codename:Strada
TypeScript 7
(Native): Once the native codebase achieves sufficient parity with the JavaScript
codebase, it will be released as TypeScript 7.0
.
- Native codebase codename:
Corsa
The two codebases will coexist for a long time.
What Needs to be Migrated
It's helpful to understand which parts of the TypeScript
ecosystem need to be migrated:
- The command-line
TypeScript
compiler - The
TypeScript
language server (helps editors supportTypeScript
)- The
JavaScript
codebase predates the widely used language server protocol, so it doesn't use it. The new language server will use this protocol, which should make it easier for editors to supportTypeScript
.
- The
- Tools that use the
TypeScript
codebase- Interacting with the
Go
codebase requires a completely new approach:- Internal component exposure will be reduced
- Interactions now occur across processes
- Interacting with the
When Will the Native Version Be Available to the Public
Current status: tsc
is available. Still missing:
JSX
- Types via
JSDoc
- Build mode (project references)
Mid-2025: tsc
with JSX
and JSDoc
(without build mode) End of 2025: Complete tsc
and language server
Source: "A 10x Faster TypeScript"
When Did the Project Begin
First prototype developed by Anders Hejlsberg:
- Started in August 2024
- Scanner and parser took 1-1.5 months to write
- Initial approach: Manual code writing
- Later: Tools to automatically convert
TypeScript
code toGo
code - Ported code worked well (required some manual intervention)
- Data structure porting could only be done manually — because
JavaScript
objects (with flexible types) andGo
structs (with highly configurable data layouts) are very different. Additionally, they now must work in a concurrent environment — for example: TheJavaScript
codebase orders types by giving each type a sequence number when it's created. This approach doesn't work in theGo
codebase because the order of type creation is no longer deterministic due to multi-threading.
Why Choose Go Over Other Programming Languages
The TypeScript
team wanted to (primarily) port the JavaScript
codebase rather than rewrite it in a different language — for two reasons:
- The new codebase must be (primarily) a drop-in replacement for the old codebase. This is difficult to achieve through rewriting.
- Rewriting would take longer.
If we look at the requirements for the programming language used in the new codebase, some of them stem from the decision to port:
- Support for cyclic data structures — heavily used in the
TypeScript
codebase. - Garbage collection. The codebase assumes this feature.
- The
JavaScript
codebase's style is more functional than object-oriented programming, not frequently using classes. This style is similar to howGo
is written.
The remaining requirements are driven by performance and ease of use (developer experience):
- Good native code support on all major platforms.
- The language should be simple to learn.
- The language should have good tooling support.
- Control over in-memory data structure layout. With
Go
, you can use structs and create an array of structs with just one allocation (compared to multiple allocations inJavaScript
). - Good support for shared memory concurrency — this is an important element in making the code faster (more details below).
Why Not Choose C#
When asked "Why not C#
", Anders Hejlsberg mentioned the following points:
Go
is lower-level thanC#
.Go
has better support for generating native code (including specifying data structure layouts).Go
is better suited for the non-object-oriented programming style used in theJavaScript
codebase.
Where Does the 10x Performance Improvement Come From
- Half of the speed improvement comes from shared memory concurrency and using multiple cores.
- The other half comes from native code:
JavaScript
must be just-in-time compiled; it must provide great flexibility for its objects; it cannot inline objects (one allocation per array element vs. one allocation for the entire array); and so on.
JavaScript
does have concurrency capabilities through Web Workers
, but memory sharing is very limited (see SharedArrayBuffer
).
TypeScript
compilation has the following phases:
- Parsing: Generate Abstract Syntax Tree (AST)
- Binding: Create "symbol tables" for declarations, set up control flow graphs, etc.
- Type checking
- Emit: Code generation
In the native codebase, parsing and binding can be done independently (no memory sharing needed). Then, the data structures are immutable and can be easily shared between threads.
Parsing, binding, and emitting speed scales linearly with the number of cores used. Together, they account for about one-third of the total compilation time.
Type checking takes up the remaining two-thirds and is less easily parallelizable. Therefore, the following tricks are used:
- Type checking works on individual files — it lazily loads more information as needed.
- Technique: Run multiple type checkers, assigning parts of the files to each checker.
- Thread safety requirements are not high: checkers only share immutable ASTs. There is some duplicate work, but not much, as most type information is local.
- On the other hand, threads cannot operate completely independently — for example, error messages should not be duplicated and should be displayed in a deterministic order.
- Using 4 checkers (current hardcoded number), memory usage increases by 20% due to work duplication, but checking speed improves by 2-3x.
- Note that this 20% is relative to single-checker
Go
— and single-checkerGo
uses only half the memory of theJavaScript
codebase. - In testing, using 8 checkers only brought an additional 20% speed improvement (source).
Can the Native Codebase Run in WebAssembly
Supporting WebAssembly
is important because it enables use cases like online TypeScript
playgrounds. It's actually already supported.
Kevin Deng wrote a "TypeScript Go Playground" using the new codebase compiled to WebAssembly
.
Work is ongoing to improve Go
's WebAssembly
output size and performance — that is: they can and will get better. Related discussion on GitHub: "Go Wasm performance"
Conclusion: An Impressive Achievement
I'm impressed by how quickly the TypeScript
team was able to port the JavaScript
codebase to Go
. In a podcast (see "Sources" below), Hejlsberg said "I started prototyping in August". I didn't expect he meant 2024, but rather 2023 or even earlier.
This speed proves the cleverness of the team's approach: if they had rewritten the JavaScript
codebase instead of porting it, it might have taken years and led to many inconsistencies between the codebases.
An interesting concern expressed by Anders Hejlsberg is that as type checking becomes faster, people might stop trying to write types that can be computed quickly — for example, it's easy to create types using template literal types that require significant computational power.
Perhaps we'll eventually get tools to analyze type performance. Type-level debugging also seems useful.
Sources
- Video "Syntax podcast: Typescript Just Got 10× Faster", hosted by Wes Bos and Scott Tolinski, featuring Anders Hejlsberg and Daniel Rosenwasser
- Video "TypeScript is being ported to Go | interview with Anders Hejlsberg", produced by Dimitri Mitropoulos for Michigan TypeScript
- Blog post "A 10x Faster TypeScript", by Anders Hejlsberg
- Video "Anders Hejlsberg on TypeScript's Go Port", hosted by Matt Pocock
- GitHub discussion: "Why Go?"