An update on Angular’s TypeScript 7-powered Compiler
Alex Rickabaugh & Mark Techson
[Alex] I joined the Angular team in 2015, around the original Angular 2.0 release. One of my first tasks was to convert source code from JavaScript into a relatively new (at the time) language from Microsoft, known as TypeScript. TypeScript would go on to become both an industry standard and one of Angular’s greatest strengths, providing the structure and safety for the team to scale the framework. We built Angular’s innovative ahead-of-time compiler on top of the TypeScript compiler’s APIs. While compiling web applications was not new at Google, it was certainly rare in the larger web ecosystem at the time.
Fast forward to today — TypeScript has helped Angular scale to some of the largest web applications in the world. The web ecosystem’s tooling has evolved significantly in the last 10 years as well. Recently we’ve started to see a push to build higher performance JavaScript compilers, bundlers, and other tools using native code. TypeScript 7 is an incredible demonstration of the potential here, and we’d like to congratulate the TypeScript team on their stable release and the amazing performance it delivers! If you haven’t seen their blog post yet, take a moment to check it out, especially the performance numbers.
Angular’s compiler has one of the most complex integrations with TypeScript in the web ecosystem, and we’ve known that this deep integration cannot work with a Go-compiled version of TypeScript. Apart from the overhead of crossing the language barrier, the Angular compiler implements its own code transformations via the ts.Transformer API which is not available cross-language. Bringing the same benefits of native tooling and TypeScript 7 to Angular developers requires us to think outside of the box. Early this year, we started putting together our plan: decouple Angular compilation from TypeScript’s compiler APIs, and build a new Angular-specific compiler that processes components, directives, etc. and outputs transformed TypeScript code, ready for processing by a build pipeline or any other compilation tool. This is a well-trodden path, and many other frameworks in the web space have similarly chosen to decouple code generation from type-checking. What’s more, many of them are using the same underlying library to perform those transformations: oxc.
Oxc, the Oxidation Compiler, is a native toolchain for JavaScript parsing and compilation developed by Void Zero. It’s written in Rust, with a stable and mature API for operations like parsing, AST visitation, semantic analysis, and code transformations. Oxc is the engine that powers their popular Vite build tool. So as cliche as it sounds… we’re rewriting the Angular compiler in Rust 🦀 (partially).
At least in development, we’re calling this new tool the Angular Preprocessor (ngp) to distinguish it from the existing TypeScript-based compiler (ngc). Let’s take a look at what this will look like from a high level:
Behind the scenes
ngp has two main tasks: compile Angular decorators (@Component, @Pipe, etc) and any associated templates for efficient rendering at runtime, and facilitate type-checking of expressions in component templates by the TypeScript compiler. For every input source file (e.g. dashboard.ts) in your project, ngp generates two output files, one for each task:
- A dashboard.ng.ts file which contains your code, but with the Angular decorators replaced with their compiled versions. This file can be fed to TypeScript or directly to a bundler like esbuild. This is the code for your components that gets loaded and executed in a browser.
- A dashboard.ngtypecheck.ts file which contains a translation of the expressions and types in any component templates, that allows TypeScript to perform its type-checking and report high-quality diagnostics.
Source maps are generated for both files, which allow any downstream tools (like TypeScript) to report errors in the context of your original input file.

Note that for performance, these output files are usually produced only in memory and not written to disk.
The Hybrid Architecture
While we eventually want to replace the entire compilation chain with native Rust code, Angular has a sophisticated template compilation pipeline written in TypeScript. Rewriting that piece would take extra time. So for ngp, we’re using a hybrid approach: Rust for the compiler’s frontend, and TypeScript for the backend.
The compiler’s frontend is a Rust library we call the analyzer. Using oxc, it parses your code, finds Angular-decorated classes, maps relationships between NgModules and their components/directives, extracts dependency information from Angular Package Format libraries, and produces a list of compilation tasks. Because Rust and Oxc have strong support for multithreading, our analyzer reads your source code in parallel and streams compilation work units to the backend.
The backend is the piece that executes those tasks, by invoking Angular’s existing template compiler to generate output TypeScript code, either for runtime or for type-checking. It receives compilation tasks from the analyzer, generates code, and writes the output files and their sourcemaps. The ngp backend is written in TypeScript.
We’re leveraging a tool called napi-rs, which allows Rust libraries to be packaged as native plugins to node applications, or alternatively as WebAssembly bundles. Using napi-rs, when ngp loads the analyzer code it can load it either as a native library if one exists for your architecture and platform, or fall back on a WASM bundle. Supporting WASM also allows the Rust-based analyzer to be loaded in browser environments, which is useful for online coding tools that need to compile Angular in a browser.
Current Status
ngp is nearing its MVP as a compiler. We’re testing it against Google’s large corpus of Angular applications in order to iterate on its correctness. We’re also prototyping its integration into the CLI’s build system, and we also have a prototype of the language service integration. We’re aiming to ship an experimental version that you can test out in your own projects later this year. Until then, let us know what questions you have about this exciting new compiler.
FAQ
Is the new compiler faster? Does it get the same 10x speedup as TypeScript 7?
It’s too early to say. TypeScript compilation is only a part of the whole type-checking, transpilation, minification, and bundling process. We’re going to refrain from drawing any conclusions until we can benchmark the full build process with the Angular CLI in an apples-to-apples comparison, but we’ve done some ad-hoc testing and the results are encouraging.
What about the language service?
We will be able to use the new ngp engine to power the Angular language service as well, and have a working prototype of this integration.
Are there going to be breaking changes?
We are testing the new compiler against Google’s entire Angular codebase, and fixing any compatibility problems we find. That said, there are a few edge cases we’re aware of where type checking behaves slightly differently with the new output in a way that could lead to new type errors surfacing. These have been exceptionally rare, but we will still document them as breaking changes to be thorough. TypeScript 7 itself has several such differences in behavior.
Why not use TypeScript’s interop APIs to port the existing compiler?
We are planning to use TS 7.1 interop APIs for type-checking and diagnostics as a part of our solution. We’ve been working closely with the TypeScript team at Microsoft to ensure that TS 7.1’s APIs can support our use cases, and we’re grateful to them for their collaboration!
We considered using the interop API layer for the whole compiler pipeline, but decided against it for performance reasons. Angular’s compiler does much more extensive AST walking and processing than other consumers, and we (like the TypeScript team) felt that the benefits of processing in native code were too large to ignore.
Why not Go, like Microsoft chose?
The TypeScript team has done a fantastic job documenting their reasons for selecting Go. Largely this boils down to Go being a much more natural fit for porting a complex codebase from another garbage collected language. This wasn’t really a constraint for Angular, since our compiler has much more straightforward data structures to manage. Instead, the main deciding factor for us was the availability of a high quality, well maintained JavaScript/TypeScript toolchain: a library with a parser, AST, semantic binder, and code transformer. The intersection of this requirement and our desire to build in native code led us to oxc and Rust.
Note: TypeScript 7 itself is a TypeScript parsing and transformation toolchain in Go, but its APIs are private by design (at least in the initial release). Currently there is no public library which implements a TypeScript parser and AST in Go.
Didn’t VoidZero already build an Angular compiler in Rust/oxc?
Yes! But with some caveats. oxc-angular-compiler focuses on Angular source code transpilation (the compiler’s task #1) but does not implement either template type-checking (task #2) or cross-file optimizations that are required to keep non-standalone application bundles small. We need to support both of these operations.
Longer term, we are interested in adapting oxc-angular-compiler’s port of our template parsing and compilation engine to move more of ngp’s work into Rust.
An update on Angular’s TypeScript 7-powered Compiler was originally published in Angular Blog on Medium, where people are continuing the conversation by highlighting and responding to this story.