tsrs vs TypeScript 7: Benchmarking the Rust Port of the TypeScript Compiler
Benchmarks of tsrs, the Rust port of the TypeScript 7 compiler, against tsgo 7.0.2, 7.1 beta and tsc 5.9 on four projects: speed, memory, threads and diagnostics.
On a 4 vCPU machine, tsrs 0.18.0 type-checked four projects 3.9 to 4.9 times faster than the TypeScript 7.1 beta, and 4.8 to 6.5 times faster than TypeScript 7.0.2. It used 2 to 3 times less peak memory than 7.1 beta and 3 to 4.6 times less than 7.0.2. On the VS Code codebase (10,427 files, 3.5 million lines), the check dropped from 32 seconds with TypeScript 7.0.2 to 5.6 seconds. Diagnostics matched the 7.1 beta exactly on two projects. On the other two, error locations and codes matched, but a few messages printed types differently.
That makes tsrs worth trying in CI now, as a second check next to tsc. It does not yet make it a replacement. The published release is a pre-1.0 port of a TypeScript development commit. The defaults are also changing in the next release, and that changes what “tsrs speed” means.
This post covers what tsrs is, how I measured it, the numbers, and where the speedup comes from. Every command is included, so the results can be checked on other hardware.
What tsrs Is#
tsrs (opens in new tab) is a Rust port of the TypeScript 7 compiler and language server. It lives under the Oxc project (opens in new tab), and Cloudflare and VoidZero work on it with the original author, Max Schwenk. The Cloudflare announcement (opens in new tab) says the work happens “in close collaboration with the TypeScript team” and states: “we are not forking TypeScript.”
The important detail is what it ports. TypeScript 7 is itself a port: Microsoft rewrote the JavaScript compiler in Go, and TypeScript 7.0 (opens in new tab) shipped that Go compiler as the regular typescript package. tsrs ports the Go code function by function. Its compatibility page (opens in new tab) says “The Go code is the spec”. Any difference in diagnostics, their order, message text or exit code counts as a tsrs bug.
Because of this, tsrs is a drop-in for TypeScript 7, not for TypeScript 5. Options that TypeScript 7 removed are rejected in the same way. This site’s own tsconfig.json failed on both compilers with the same error:
tsconfig.json(13,5): error TS5102: Option 'baseUrl' has been removed. Please remove it from your configuration.
So the TypeScript 7 migration comes first. tsrs is a choice you make after that.
How I Measured#
The setup follows the tsrs benchmark methodology (opens in new tab) where possible. The same pinned project commits and the same compiler flags are used, at a smaller scale.
Machine. A cloud VM with 4 vCPUs (Intel Xeon at 2.1 GHz), 15 GB RAM and Linux x86_64. A shared VM is noisier than dedicated hardware, so I report medians and the standard deviation from hyperfine (opens in new tab).
Compilers.
| Compiler | Package | Notes |
|---|---|---|
| tsrs | tsrs@0.18.0-ts7.1.0-dev.20260929 | Latest published release; ports TypeScript commit b85298b6 (7.1 dev) |
| tsgo 7.1 beta | typescript@7.1.0-beta | Closest official build to what tsrs ports |
| tsgo 7.0.2 | typescript@7.0.2 | Current stable TypeScript |
| tsc 5.9 | typescript@5.9 | The JavaScript compiler, for context |
For the native compilers I called the platform binaries directly, so no Node launcher time is included.
Projects. Three come from the tsrs benchmark catalog at its pinned commits. The fourth is this site.
| Project | Files | Lines | Types |
|---|---|---|---|
| sph.sh (Astro site, this blog) | 866 | 246,452 | 60,106 |
| xstate | 1,536 | 398,462 | 373,803 |
| webpack | 1,607 | 656,348 | 588,331 |
VS Code (src) | 10,427 | 3,510,944 | 2,866,525 |
File and line counts include declaration files from node_modules, which is why a small site reaches 246k lines.
Command. Every timed run used the same flags:
<compiler> -p <project> --noEmit --incremental false --pretty false
Each compiler ran with its own defaults. On 4 vCPUs that means 4 checker threads for every native compiler: tsgo always defaults to 4, and tsrs 0.18 uses one per core. The thread count is therefore equal, which is the comparison the Cloudflare post also asks for. A second pass used --singleThreaded for all of them.
Peak memory is the maximum resident set size from GNU time, as the median of three runs.
Results: Default Settings#
Wall time (median; 10 runs, 5 for VS Code; tsc 5.9 had 2 to 3 runs, and 1 run on VS Code):
| Project | tsc 5.9 | tsgo 7.0.2 | tsgo 7.1 beta | tsrs 0.18 | tsrs vs 7.1 beta | tsrs vs 7.0.2 |
|---|---|---|---|---|---|---|
| sph.sh | 3.71 s | 0.63 s | 0.52 s | 0.13 s | 3.9x | 4.8x |
| xstate | 13.86 s | 2.48 s | 1.92 s | 0.39 s | 4.9x | 6.3x |
| webpack | 22.23 s | 4.18 s | 3.16 s | 0.65 s | 4.9x | 6.5x |
| VS Code | 252 s | 31.99 s | 22.38 s | 5.59 s | 4.0x | 5.7x |
Peak memory (median of 3 runs; tsc 5.9 from one run):
| Project | tsc 5.9 | tsgo 7.0.2 | tsgo 7.1 beta | tsrs 0.18 | tsrs vs 7.1 beta | tsrs vs 7.0.2 |
|---|---|---|---|---|---|---|
| sph.sh | 390 MiB | 249 MiB | 194 MiB | 96 MiB | 2.0x less | 2.6x less |
| xstate | 850 MiB | 752 MiB | 578 MiB | 224 MiB | 2.6x less | 3.4x less |
| webpack | 1,294 MiB | 1,258 MiB | 862 MiB | 375 MiB | 2.3x less | 3.4x less |
| VS Code | 8,518 MiB | 8,210 MiB | 5,542 MiB | 1,801 MiB | 3.1x less | 4.6x less |
Some observations:
- The move from tsc 5.9 to TypeScript 7 is still the larger step in absolute time. On VS Code it cuts 220 seconds. tsrs then cuts another 17 to 26 seconds. For a small project like this site, every native option finishes in under a second.
- TypeScript 7.1 beta is 1.2 to 1.4 times faster than 7.0.2 here. The Cloudflare post notes that some of those optimizations came from tsrs. Comparing tsrs only against 7.0.2 makes the gap look larger than it is against current TypeScript.
- Memory may matter more than speed in CI. A VS Code-sized check needs about 8 GB with 7.0.2 and 1.8 GB with tsrs. That decides whether the check fits on a standard runner at all.
Results: Single-Threaded#
With --singleThreaded, each compiler does the same work on one core. That separates the code’s efficiency from how well it uses threads.
| Project | tsgo 7.0.2 | tsgo 7.1 beta | tsrs 0.18 | tsrs vs 7.1 beta |
|---|---|---|---|---|
| sph.sh | 0.77 s | 0.67 s | 0.27 s | 2.5x |
| xstate | 3.69 s | 3.03 s | 1.14 s | 2.7x |
| webpack | 6.14 s | 4.90 s | 1.97 s | 2.5x |
| VS Code | 57.77 s | 42.87 s | 18.90 s | 2.3x |
On one core, tsrs is about 2.5 times faster than the 7.1 beta. On VS Code, single-threaded tsrs (18.9 s) also beats four-threaded tsgo 7.0.2 (32 s).
Where the Speedup Comes From#
Comparing both tables shows two separate effects. The parallel speedup below is single-threaded time divided by default (4-thread) time:
| Project | tsgo 7.0.2 | tsgo 7.1 beta | tsrs 0.18 |
|---|---|---|---|
| sph.sh | 1.2x | 1.3x | 2.0x |
| xstate | 1.5x | 1.6x | 2.9x |
| webpack | 1.5x | 1.6x | 3.0x |
| VS Code | 1.8x | 1.9x | 3.4x |
So the 4 to 5 times gap on 4 cores splits into roughly two parts:
- About 2.5 times from per-core efficiency. The same algorithm runs in Rust instead of Go. The tsrs Rust notes (opens in new tab) list per-thread arenas instead of a garbage collector, packed data layouts, mimalloc and profile-guided optimization. I did not profile this, so treat it as the project’s explanation, not a measured breakdown.
- About 1.6 to 1.9 times from better use of the 4 threads. tsgo checks with 4 checkers and a fixed file assignment. With 4 checkers, it gets less than 2 times out of 4 cores. tsrs 0.18 adds locality scheduling, work stealing and lazy type members by default, and gets 3 to 3.4 times on larger projects.
The second part is the one that changes in the next release.
The Default Behavior Is Changing#
In 0.18.0, tsrs runs its optimizations by default. The 0.19.0 beta candidate reverses this. Its release notes (opens in new tab) describe the new default as “the pinned TypeScript defaults: four checkers, Go’s file assignment and checker history, and eager members.” The scheduling and memory optimizations move behind an opt-in --fast flag or TSRS_FAST=1.
The README (opens in new tab) lists the trade-offs of fast mode:
- Check order can change which errors appear, in known recursive-type and instantiation-limit cases. The exit status can change too.
- Fast mode can use more CPU, and with more checkers, more memory.
- Type and instantiation counters can differ from TypeScript’s.
This is a sensible split. Default mode targets exact parity, and fast mode targets speed. It also means the numbers above are closest to 0.19’s --fast mode, not its default. I would expect default 0.19 to land between the single-threaded ratio and the 4-thread ratio, but I did not measure an unreleased build. Measure your own project after upgrading.
One more note on headline numbers. The upstream benchmark results (opens in new tab) show up to 16.9 times faster than tsgo 7.0.2. Those runs used 16 vCPUs, with tsrs at 16 checkers and tsgo at its default of 4. That is a fair “both at defaults” comparison on large hardware. On a 4-core CI runner, both use 4 checkers, and the gap is smaller. The core count of your machine shapes the result as much as the compiler does.
Diagnostics: Same Errors, Some Different Text#
Speed means little if the errors differ. I compared full diagnostic output from tsrs 0.18 and tsgo 7.1 beta on each project:
| Project | Errors (tsrs / 7.1 beta / 7.0.2) | Output vs 7.1 beta |
|---|---|---|
| sph.sh | 79 / 79 / 79 | Same errors; one elaboration line prints a type differently |
| xstate | 0 / 0 / 0 | Byte-identical |
| webpack | 840 / 840 / 848 | Same file, line, column and code for all 840; 6 messages print types differently |
| VS Code | 371 / 371 / 359 | Byte-identical |
The differences on webpack are in type printing. A long type like plugins: ModuleFederationPlugin[] is shortened to plugins: ... at a different point. Some nested elaboration lines are also present in one output and missing in the other. tsgo 7.1 beta and tsrs each gave the same output across repeated runs. tsrs 0.18 ports a 7.1 dev commit, not the 7.1 beta tag. Part of this gap may come from that version difference, and I cannot separate the two causes from these runs.
The error counts against 7.0.2 differ more (848 vs 840 on webpack, 359 vs 371 on VS Code). The tsrs results page shows that typescript@7.1.0-dev.20260930.4, built from the commit tsrs ports, reports exactly the tsrs errors. That difference comes from changes between TypeScript versions, not from tsrs.
For most teams, the practical risk is narrow. Anything that compares error message text exactly, such as snapshot tests of compiler output, can break. A pass/fail type check is much less exposed.
What I Would Do With This#
- Small projects. This site checks in 0.5 seconds with TypeScript 7.1 beta. Saving 0.4 seconds does not justify a second compiler.
- Large projects and monorepos. Here tsrs changes the CI picture. VS Code-scale checks go from about 22 to 32 seconds down to about 6, and from 5 to 8 GB of memory down to under 2 GB. Smaller runners become an option.
- How to adopt. Run tsrs as a separate, non-blocking job first, and keep
tscas the gate. Pin an exact tsrs version, because the defaults change between releases. Check that exit codes and error locations match on your code. Promote it only after that holds over a few releases. - Editors. tsrs also ships a language server. I did not benchmark editor latency here, so I cannot say how it compares in daily use.
The Oxc toolchain has followed the same pattern for linting and formatting. Native tools first prove parity next to the existing tool, then take over once the gap is closed.
Reproducing the Benchmark#
The full setup fits in a short script. It needs Node, git, hyperfine and GNU time:
mkdir tsbench && cd tsbench
npm init -y >/dev/null
npm i tsrs@0.18.0-ts7.1.0-dev.20260929 \
ts70@npm:typescript@7.0.2 ts71@npm:typescript@7.1.0-beta
TSRS=node_modules/@tsrs/linux-x64-gnu/tsrs
TSGO70=node_modules/@typescript/typescript-linux-x64/lib/tsc
TSGO71=node_modules/ts71/node_modules/@typescript/typescript-linux-x64/lib/tsc
# Use the commit pinned in tsrs bench/projects.json
git clone https://github.com/webpack/webpack.git && cd webpack
git checkout b54c5e2aa26dd0267bc904d45402f811f100186c
npx yarn@1.22.22 install --ignore-scripts --ignore-engines
npx yarn@1.22.22 link && npx yarn@1.22.22 link webpack
ARGS="-p . --noEmit --incremental false --pretty false"
# -i: type errors exit with code 1 or 2, which is expected here
hyperfine -N -i --warmup 1 --runs 10 \
-n tsrs "../$TSRS $ARGS" \
-n tsgo-7.0.2 "../$TSGO70 $ARGS" \
-n tsgo-7.1-beta "../$TSGO71 $ARGS"
# Peak memory (max RSS in KB)
/usr/bin/time -f "%M KB" "../$TSRS" $ARGS > /dev/null
Platform package paths differ on macOS and on musl Linux. Check node_modules/@tsrs/ and node_modules/@typescript/ for the right binary.
Conclusion#
tsrs delivers on its main claim. On equal threads it was 4 to 5 times faster than the TypeScript 7.1 beta and used 2 to 3 times less memory on every project I tested. On two of four projects the output was byte-identical. That combination is unusual for a project this young, and it comes from treating the Go compiler as the spec.
Its limits are also clear. It is pre-1.0, it tracks TypeScript development commits, and the next release changes what the default mode does. A large share of the speedup on multi-core machines comes from the scheduling that becomes opt-in. For large codebases, it is worth running next to tsc now. For small ones, TypeScript 7 alone already does the job.
References#
- tsrs on GitHub (opens in new tab) - Source, README and fast-mode documentation for the Rust port
- A Rust port of TypeScript (Cloudflare Blog) (opens in new tab) - Announcement covering the motivation, the collaboration with the TypeScript team and headline numbers
- tsrs compatibility and evidence (opens in new tab) - Conformance numbers, the “Go code is the spec” rule and known gaps
- tsrs release notes (opens in new tab) - 0.18.0 and 0.19.0 candidate notes, including the change of default checker settings
- tsrs benchmark methodology (opens in new tab) - Pinned projects, compiler flags and how the upstream benchmarks are run
- tsrs benchmark results (opens in new tab) - Upstream 16 vCPU results against tsgo 7.0.2 and bun check
- tsrs Rust implementation notes (opens in new tab) - Memory model and build-level optimizations: arenas, packed layouts, mimalloc, PGO
- tsrs usage and performance options (opens in new tab) - Checker counts, memory targets and fast-mode side effects
- Announcing TypeScript 7.0 (opens in new tab) - Official release of the Go-based TypeScript compiler
- A 10x Faster TypeScript (opens in new tab) - The original announcement of the native Go port
- microsoft/typescript-benchmarking (opens in new tab) - The benchmark suite the TypeScript team uses, source of several pinned projects
- hyperfine (opens in new tab) - Command-line benchmarking tool used for all timing runs
- Oxc (opens in new tab) - The JavaScript toolchain project that hosts tsrs
Related posts
How native tools like esbuild, SWC, and Vite solved webpack's speed problems, taking builds from tens of seconds down to milliseconds.
esbuild · build-tools · vite +4
Nub and Vite+ are both 2026 oxc-powered Rust toolchains that look like rivals but are not. A clear rule for which binary belongs in which repo.
javascript · typescript · vite +3
How Google's 2009 Closure Compiler shaped modern web tooling, from dead code elimination to type checking, and its lasting mark on today's build tools.
javascript · build-tools · best-practices +6
A measured benchmark of 9 bundlers and 3 cdk synth runners for CDK TypeScript Lambdas, with a per-layer default and the rule that picks each one.
aws-cdk · lambda · typescript +3
DI containers, monolithic SDKs, god-handlers, top-level secret fetches, and heavy ORMs: what they cost on cold start, and the functional shape that replaces them.
lambda · typescript · serverless +2