Benchmarks

Every number here was measured by the ts-r project and recorded in a commit message, a crate's AGENTS.md or a design record. Each section says what was measured, on what machine and project, how many runs, the command that reproduces it and the commit that recorded it. Pre-alpha: only the pieces that exist today are measured, and the numbers that miss their targets are shown too. Lower is better unless a chart says otherwise.

ts-r-dev against Vite 8 on excalidraw

excalidraw (about 500 TypeScript modules served from source, 47 dependency entries) served by ts-r-dev and by Vite 8.3.4 with @vitejs/plugin-react 6.1.2. Start is process spawn until the canvas and welcome screen are in the page; cold means an empty ts-r cache and no Vite dependency cache. Both kept React state through all 53 edits with 0 full reloads. SCSS is replaced by an empty stylesheet for both servers (ts-r-dev does not compile it yet).

Start to first render, medianShorter is faster, in ms
  • ts-r
  • Other tools
  • ts-r-dev, warm538 ms
  • ts-r-dev, cold939 ms
  • Vite 8.3.4, warm1,032 ms
  • Vite 8.3.4, cold1,107 ms
Show as table
Start to first render, median (ms, lower is better)
SetupMedianNotes
ts-r-dev, warm538 ms
ts-r-dev, cold939 msincludes pre-bundling 47 dependency entries into 133 files
Vite 8.3.4, warm1,032 ms
Vite 8.3.4, cold1,107 ms
Save to page updated, p50Shorter is faster, in ms
  • ts-r
  • Other tools
  • target < 30 ms
  • ts-r-dev8.6 ms
  • Vite 8.3.435 ms
Show as table
Save to page updated, p50 (ms, lower is better)
Setupp50p95
ts-r-dev8.6 ms9.0 ms
Vite 8.3.435 ms37.2 ms
Dev server memory after the editsShorter is faster, in MB
  • ts-r
  • Other tools
  • ts-r-dev153 MB
  • Vite 8.3.4364 MB
Show as table
Dev server memory after the edits (MB, lower is better)
SetupRSS after 50 editsAfter first loadNotes
ts-r-dev153 MB151 MBincludes the Node process that runs vite.config
Vite 8.3.4364 MB371 MB
How we measured
Measured
Headless Chrome (a fresh one per page load) drives both servers: 5 cold and 5 warm starts (median), then 50 measured edits of the welcome-screen heading after 3 warm-ups, timed in the browser. RSS is the whole server process group.
Machine
Apple M-series MacBook (macOS arm64, 16 cores, 48 GB), shared with other jobs while measuring; release build, 4 threads
Software
ts-r-dev release build; Vite 8.3.4 with @vitejs/plugin-react 6.1.2 pinned in bench/package.json; both servers capped at 4 threads
Input
excalidraw at the commit pinned in bench/repos.toml, with the harness in bench/dev/excalidraw (index.html, main.tsx rendering excalidraw-app/App, vite.config.mjs with its source aliases)
Command
cargo xtask bench --fetch-repos --repos excalidraw && scripts/limit.sh --mem-mb 1024 --time-s 1800 -- node bench/dev/dev.mjs --app excalidraw --server both --mode bench
Recorded in
commit 9c3a371, crates/ts_r_hmr/AGENTS.md, bench/dev/dev.mjs

ts-r-dev: hot reload latency as the app grows

Time from saving a file to the browser having re-imported the changed module and run its callbacks, on generated apps. The plan's target is under 30 ms at p50, staying flat as the project grows. Vite has not been measured on these generated apps yet.

Save to browser updated, p50, as the app growsLower is faster, in ms. Generated apps from 1,000 to 50,000 modules.
Target: under 30 ms
  • 4.9 ms at 1,000 modules
  • 4.8–5.0 ms at 10,000 modules
  • 5.4 ms at 50,000 modules
Show as table
Save to browser updated, p50 (ms, lower is better)
Setupp50p95First save of a fileInitial page load
1,000 modules4.9 ms5.9–6.9 ms7–17 ms0.3 s
10,000 modules4.8–5.0 ms5.3–6.4 ms7–17 ms1.7 s
50,000 modules5.4 ms8.3 ms16 ms13.5 s
How we measured
Measured
Browser-side timing in headless Chrome: 47 measured edits per size after 3 warm-up edits. Ranges are across runs; the bar shows the upper end.
Machine
Apple M-series Mac, macOS arm64, release build (the exact chip was not recorded)
Software
ts-r-dev release build, headless Chrome
Input
Generated JavaScript apps of 1,000, 10,000 and 50,000 modules; one self-accepting module under a 100-wide import tree is edited. The first save of a file goes through FSEvents, later saves through the kqueue fast path.
Command
TS_R_LATENCY_SIZES=1000,10000,50000 cargo test --release -p ts_r_dev --test latency -- --ignored --nocapture
Recorded in
commit 9ed299f, crates/ts_r_hmr/AGENTS.md

ts-r-build against esbuild 0.28.2

Bundling two public projects: three.js x10 (ten copies of three.js, minified IIFE with source maps) and the excalidraw app (minified ESM). Only three.js x10 is drawn as bars: both bundlers produce the same exports from the same input. Without its persistent cache ts-r-build is slower than esbuild there. On excalidraw ts-r-build does less work than esbuild: it does not bundle template-literal import() yet, so it leaves out the 61 locale chunks esbuild emits (7.2 MB of output against 9.3 MB). Those runs, the single bundle and the 20 rebuilds, are in the table with that note, not drawn next to esbuild. Rolldown and Vite's production build have not been measured.

Bundle three.js x10, minifiedShorter is faster, in ms
  • ts-r
  • Other tools
  • esbuild 0.28.2225 ms
  • ts-r-build, persistent cache241 ms
  • ts-r-build, no cache378 ms
Show as table
Bundle three.js x10, minified (ms, lower is better)
SetupMedian of 10 runs
esbuild 0.28.2225 ms
ts-r-build, persistent cache241 ms
ts-r-build, no cache378 ms
excalidraw (not like-for-like), memory, output and the oracle
Whatts-r-buildesbuild 0.28.2Notes
Bundle excalidraw, minified: no cache / persistent cache255 / 132 ms201 msMedian of 10 runs. Not like-for-like: ts-r-build leaves out the 61 locale chunks esbuild emits (7.2 MB of output against 9.3 MB)
excalidraw: first build plus 20 edit-and-rebuild cycles, one process1,358 ms3,457 ms (context.rebuild)Median of 5 runs. Not like-for-like: every build leaves out the 61 locale chunks
Peak memory, excalidraw, no cache / cached317 / 152 MB624 MBWithout the 61 locale chunks
Peak memory, three.js x10, no cache / cached459 / 275 MB695 MB
three.js x10 output7.88 MB (+2.2%), same exports7.71 MB
esbuild's own bundler tests (oracle)643 of 746 compared cases pass (86.2%)reference
How we measured
Measured
Wall time of whole processes with hyperfine (cargo xtask bench --repos), median; peak RSS of one extra run (--memory). Rebuilds: examples/build_loop.rs --edit against bench/js/esbuild-rebuild.mjs, editing excalidraw-app/components/AppWelcomeScreen.tsx
Machine
Apple M-series MacBook (macOS arm64, 16 cores, 48 GB), shared with other jobs while measuring; release build, 4 threads
Software
ts-r-build release build with 4 threads; esbuild 0.28.2 pinned in bench/package.json
Input
excalidraw (bench/dev/excalidraw/main.tsx, .woff2 as files, .scss empty) and three.js x10 at the commits pinned in bench/repos.toml
Command
cargo xtask bench --fetch-repos --repos excalidraw,three-x10 && cargo xtask bench --repos excalidraw,three-x10 --memory
Recorded in
commit 9c3a371, bench/README.md, bench/scenarios.toml

ts-r-lint against ESLint with the same rules

tRPC's own ESLint config with every rule ts-r-lint does not implement removed, given to both tools as the same file, on the same 147 files: 48 rules, without type information on either side. Both report 0 problems from those rules. The bars are the run where ts-r-lint evaluates the config in Node every time, as ESLint does; most of its time is that Node process. By default ts-r-lint caches the evaluated config and then takes 15.5 ms, but ESLint has no such step to skip, so that run is in the table, not drawn next to ESLint. With type-aware rules the runs are not comparable yet: on tRPC ts-r-lint's parity gate turns those rules off (its type check reports one error tsc 7 does not), and with the gate off it reports 11 problems ESLint does not.

tRPC, same 48 rules, config evaluated each runShorter is faster, in ms
  • ts-r
  • Other tools
  • ts-r-lint, config not cached733 ms
  • ESLint 10.10.01,015 ms
Show as table
tRPC, same 48 rules, config evaluated each run (ms, lower is better)
SetupMedian of 5 runsNotes
ts-r-lint, config not cached733 msNode evaluates eslint.config.js on every run
ESLint 10.10.01,015 ms
Cached config, memory, and the type-aware rules
RunTimePeak memoryNotes
ts-r-lint, same 48 rules, config cached (its default)15.5 ms9 MBSkips evaluating the config, which ESLint does on every run, so not drawn next to ESLint
ts-r-lint, same 48 rules, config not cached733 ms336 MBPeak is the Node process that evaluates the config
ESLint, same 48 rules1,015 ms515 MB0 problems from the rules, like ts-r-lint
ts-r-lint, same 80 rules with the 32 type-aware ones, parity gate off, empty cache952 ms335 MBNot like-for-like: 11 problems ESLint does not report (unbound-method 4, no-unnecessary-type-assertion 4, switch-exhaustiveness-check 2, dot-notation 1). By default the parity gate turns these rules off on tRPC
ESLint, same 80 rules (typescript-eslint projectService)2,605 ms973 MB0 problems from the rules
How we measured
Measured
Wall time of the whole command with hyperfine (cargo xtask bench --repos trpc lint-trpc-same-rules), median of 5 runs after 1 warm-up; peak RSS of the largest process in one extra run. Before each tool's runs, bench/js/eslint-same-rules.mjs writes the restricted configs from the binary's --list-rules. Untyped runs: ts-r-lint --no-type-check, ESLint with parserOptions project and projectService off. Problems compared once from each tool's JSON output by file, rule, line and column
Machine
Apple M-series MacBook (macOS arm64, 16 cores, 48 GB), shared with other jobs while measuring; release build, 4 threads
Software
ts-r-lint release build (crates as of a7210058) with 4 threads; ESLint 10.10.0 and typescript-eslint 8.70.0 from tRPC's lockfile; Node 24.21.0
Input
tRPC packages/server/src and packages/client/src (147 files) at the commit pinned in bench/repos.toml; tRPC's eslint.config.js limited to the 80 of its 126 configured rules ts-r-lint implements (48 without the type-aware ones)
Command
cargo xtask bench --fetch-repos --repos trpc && RAYON_NUM_THREADS=4 cargo xtask bench --repos trpc lint-trpc-same-rules --memory
Recorded in
commit a6bb4b48, bench/js/eslint-same-rules.mjs, bench/scenarios.toml, bench/README.md

ts-r-lint against ESLint and oxlint

Each project linted with its own config. Not like-for-like: ESLint also runs plugin rules ts-r-lint does not have (on TypeORM most of its problems come from jsdoc rules), and where ts-r-check's diagnostics differ from tsc's, ts-r-lint runs without type information (its parity gate), so its type-aware column is a type check plus the untyped rules. Because ESLint does more work, these runs are only in the table, with no bars and no totals. The rules themselves match ESLint's and typescript-eslint's test suites (see Correctness).

Each project with its own config, and memory (not like-for-like)
RunTimePeak memoryNotes
tRPC server and client sources: ts-r-lint / no type check186 / 20 ms148 / 9 MBMedian of 3 runs
tRPC: ESLint, type-aware2,566 ms992 MBAlso runs plugin rules ts-r-lint lacks, so not a like-for-like comparison
TypeORM packages/typeorm/src: ts-r-lint / no type check810 / 194 ms380 / 26 MBMedian of 3 runs
TypeORM: ESLint, type-aware6,718 ms1,748 MBMost of its problems come from jsdoc rules ts-r-lint lacks, so not a like-for-like comparison
Playwright core sources: ts-r-lint / no type check725 / 59 ms465 / 17 MB
Playwright core sources: ESLint5,441 ms2,341 MBAlso runs plugin rules ts-r-lint lacks, so not a like-for-like comparison
date-fns pkgs/core/src: ts-r-lint76 ms14 MB
date-fns pkgs/core/src: oxlint (its own config)348 ms301 MBDifferent rule set, so not a like-for-like comparison
How we measured
Measured
Wall time of the whole command with hyperfine (cargo xtask bench --repos), median of 3 to 5 runs; peak RSS of one extra run
Machine
Apple M-series MacBook (macOS arm64, 16 cores, 48 GB), shared with other jobs while measuring; release build, 4 threads
Software
ts-r-lint release build with 4 threads; each repo's own ESLint or oxlint from its lockfile
Input
tRPC, TypeORM, Playwright and date-fns at the commits pinned in bench/repos.toml, with their own lint configs
Command
cargo xtask bench --fetch-repos --repos trpc,typeorm,playwright,date-fns && cargo xtask bench --repos trpc,typeorm,playwright,date-fns --memory lint-
Recorded in
commit 9c3a371, bench/README.md, bench/scenarios.toml

ts-r-check against tsc 7.0.2

Type checking five public projects from the command line, cold (no saved history), against TypeScript 7.0.2's native tsc. These repos do not type-check cleanly under TypeScript 7, and ts-r-check does not report the same diagnostics as tsc on them yet: it reports extra ones on all five (27 on date-fns to 1,333 on RxJS in the last comparison) and misses some of tsc's. It also skips checks it does not implement yet, so these times will move as the checker grows.

Check all five projects, total wall timeShorter is faster, in ms
  • ts-r
  • Other tools
  • ts-r-check1,936 ms
  • tsc 7.0.23,044 ms
Show as table
Check all five projects, total wall time (ms, lower is better)
SetupSum of the medians
ts-r-check1,936 ms
tsc 7.0.23,044 ms
Cold check, wall timeShorter is faster, in ms
  • tRPC packages/server108 ms
  • date-fns pkgs/core226 ms
  • RxJS packages/rxjs462 ms
  • TypeORM packages/typeorm548 ms
  • Playwright592 ms
Show as table
Cold check, wall time (ms, lower is better)
Setupts-r-check, median of 5tsc 7.0.2
tRPC packages/server108 ms189 ms
date-fns pkgs/core226 ms323 ms
RxJS packages/rxjs462 ms1,280 ms
TypeORM packages/typeorm548 ms567 ms
Playwright592 ms685 ms
Cold check, peak memoryShorter is faster, in MB
  • tRPC packages/server117 MB
  • date-fns pkgs/core216 MB
  • RxJS packages/rxjs458 MB
  • TypeORM packages/typeorm377 MB
  • Playwright463 MB
Show as table
Cold check, peak memory (MB, lower is better)
Setupts-r-check, one runtsc 7.0.2
tRPC packages/server117 MB294 MB
date-fns pkgs/core216 MB558 MB
RxJS packages/rxjs458 MB1,168 MB
TypeORM packages/typeorm377 MB826 MB
Playwright463 MB1,094 MB
Warm runs, and each repo's own TypeScript
Projectts-r-check, saved history, nothing changedThe repo's own tsc
tRPC23 ms1,130 ms
date-fns78 ms324 ms
RxJS57 ms2,468 ms
TypeORM126 ms3,915 ms
Playwright40 ms4,754 ms
How we measured
Measured
Wall time with hyperfine (cargo xtask bench --repos), median of 5 runs; peak RSS of one extra run. Every tool checks the same wrapper tsconfig (noEmit, not incremental)
Machine
Apple M-series MacBook (macOS arm64, 16 cores, 48 GB), shared with other jobs while measuring; release build, 4 threads
Software
ts-r-check release build; TypeScript 7.0.2 (native tsc) pinned in bench/package.json; each repo's own TypeScript
Input
tRPC, date-fns, RxJS, TypeORM and Playwright at the commits pinned in bench/repos.toml
Command
cargo xtask bench --fetch-repos --repos trpc,date-fns,rxjs,typeorm,playwright && cargo xtask bench --repos trpc,date-fns,rxjs,typeorm,playwright --memory check-
Recorded in
commit 9c3a371, bench/README.md, bench/scenarios.toml

ts-r-lsp with our checker against tsserver and TypeScript 7's LSP

Under 500 MB on these projects; large ones not measured

A scripted LSP client on three public projects: date-fns (1,641 sources), RxJS (231) and tRPC (154). ts-r-lsp uses far less memory than tsserver and TypeScript 7's native LSP, with every source open too, but it is slower than TypeScript 7's LSP to first diagnostics on all three and per keystroke on two of them (it re-checks each edit on a fresh checker). It also reports a few diagnostics tsc does not on the opened RxJS and tRPC files. These are medium projects: the 500 MB target is for large ones, which have not been measured.

RxJS: memory with all 231 sources openShorter is faster, in MB
  • ts-r
  • Other tools
  • target ≤ 500 MB
  • ts-r-lsp377 MB
  • TypeScript 7 LSP (tsc --lsp)820 MB
  • tsserver (the repo's TypeScript)1,091 MB
Show as table
RxJS: memory with all 231 sources open (MB, lower is better)
SetupRSS
ts-r-lsp377 MB
TypeScript 7 LSP (tsc --lsp)820 MB
tsserver (the repo's TypeScript)1,091 MB
date-fns: memory with all 1,641 sources openShorter is faster, in MB
  • ts-r
  • Other tools
  • ts-r-lsp232 MB
  • TypeScript 7 LSP (tsc --lsp)710 MB
Show as table
date-fns: memory with all 1,641 sources open (MB, lower is better)
SetupRSSNotes
ts-r-lsp232 MB
TypeScript 7 LSP (tsc --lsp)710 MBtsserver did not publish diagnostics within the harness's 120 s
tRPC router.ts: keystroke to diagnostics, p50Shorter is faster, in ms · TypeScript 7 is faster here
  • ts-r
  • Other tools
  • TypeScript 7 LSP6.2 ms
  • ts-r-lsp9.4 ms
  • tsserver24.3 ms
Show as table
tRPC router.ts: keystroke to diagnostics, p50 (ms, lower is better)
Setupp50p95
TypeScript 7 LSP6.2 ms7.4 ms
ts-r-lsp9.4 ms10.5 ms
tsserver24.3 ms29.3 ms
tRPC router.ts: first diagnosticsShorter is faster, in ms · TypeScript 7 is faster here
  • ts-r
  • Other tools
  • TypeScript 7 LSP58 ms
  • ts-r-lsp224 ms
  • tsserver800 ms
Show as table
tRPC router.ts: first diagnostics (ms, lower is better)
SetupTime
TypeScript 7 LSP58 ms
ts-r-lsp224 ms
tsserver800 ms
Per project
Measurets-r-lsptsserverTypeScript 7 LSP
date-fns format/index.ts: first diagnostics416 mstimed out168 ms
date-fns: keystroke to diagnostics p50 / p9514.1 / 14.7 mstimed out5.0 / 5.8 ms
date-fns: memory, one file / all 1,641 open164 / 232 MBtimed out469 / 710 MB
RxJS merge-map.ts: first diagnostics155 ms953 ms108 ms
RxJS: keystroke to diagnostics p50 / p9512.2 / 12.8 ms28.6 / 35.1 ms13.3 / 17.2 ms
RxJS: memory, one file / all 231 open138 / 377 MB840 / 1,091 MB397 / 820 MB
tRPC router.ts: memory, one file / all 154 open95 / 165 MB571 / 757 MB477 / 480 MB
tRPC: hover p50 / p950.08 / 3.77 ms0.75 / 9.66 ms0.32 / 5.68 ms
tRPC: completion p50 / p950.08 / 0.25 ms0.38 / 12.53 ms0.22 / 0.40 ms
Diagnostics on the opened file (date-fns / RxJS / tRPC)0 / 2 / 3– / 0 / 00 / 0 / 2
How we measured
Measured
A scripted LSP client: first diagnostics, hover, definition and completion latency (20 sampled positions), keystroke to diagnostics (30 keys, 150 ms apart) on one file, then memory with that file open and with every source of the project open (5 s settle); one run per tool
Machine
Apple M-series MacBook (macOS arm64, 16 cores, 48 GB), shared with other jobs while measuring; release build, 4 threads
Software
ts-r-lsp release build with the ts-r backend and the tsc parity check off; each repo's own tsserver; TypeScript 7.0.2's native LSP (tsc --lsp) pinned in bench/package.json
Input
date-fns, RxJS and tRPC at the commits pinned in bench/repos.toml; the files and source directories are each repo's [repo.editor] set
Command
cargo xtask bench --fetch-repos --repos date-fns,rxjs,trpc && cargo xtask bench --editor --repos date-fns,rxjs,trpc (prints the commands; the editor-latency.js ones were run)
Recorded in
commit 9c3a371, bench/README.md, editors/vscode/bench/editor-latency.js

Lint in the editor: ts-r-lsp against the ESLint extension's server

Time from a keystroke to new lint diagnostics, and server memory, in tRPC's largest server source (22 KB, resolveResponse.ts) with the repo's own ESLint config and all 154 sources open. ts-r runs the rules it implements: 35 of the config's rules are not run in the editor (17 not implemented yet, 17 from plugins, 1 type-aware), so the ESLint server does more work and this is shown as a table rather than as bars.

Lint in the editor (not like-for-like: the ESLint server runs 35 rules ts-r skips)
ServerKeystroke to diagnostics, p50 / p95RSS, all 154 sources open
ts-r-lsp --lint-only15.6 / 15.8 ms (includes the default 10 ms debounce)10.6 MB
ESLint server 3.0.34 (also runs the 35 rules ts-r skips)53.3 / 58.6 ms2,296 MB
How we measured
Measured
A scripted LSP client opens every source, then types 300 keystrokes 100 ms apart into a comment and times didChange to diagnostics; RSS of the server at the end; one run each
Machine
Apple M-series MacBook (macOS arm64, 16 cores, 48 GB), shared with other jobs while measuring; release build, 4 threads
Software
ts-r-lsp release build; the ESLint VS Code extension's server 3.0.34 run headlessly with pull diagnostics; the repo's own ESLint
Input
tRPC at the commit pinned in bench/repos.toml: packages/server/src/unstable-core-do-not-import/http/resolveResponse.ts, with packages/server/src and packages/client/src open
Command
scripts/limit.sh -- node editors/vscode/bench/lint-keystrokes.js ts-r target/release/ts-r-lsp target/bench-repos/trpc <file> --open-all --src-dirs packages/server/src,packages/client/src (eslint mode with eslintServer.js for the other)
Recorded in
commit 9c3a371, bench/README.md, editors/vscode/bench/lint-keystrokes.js

ts-r-lsp: two editor windows on your own TypeScript

ts-r-lsp can also run your project's own TypeScript behind one shared server per workspace, instead of our checker. This measures total memory for two editor windows on tRPC in that mode (two files open in each, one window in packages/server and one in packages/client). The shared server holds both windows' projects, so it is larger than one plain tsserver but smaller than two.

Memory for two editor windowsShorter is faster, in MB
  • ts-r
  • Other tools
  • ts-r-lsp, one shared server698 MB
  • Two plain tsservers838 MB
  • VS Code default setup1,451 MB
Show as table
Memory for two editor windows (MB, lower is better)
SetupTotal memoryNotes
ts-r-lsp, one shared server698 MB2 editor fronts of 3 MB each, plus one daemon with one tsserver (692 MB)
Two plain tsservers838 MBone tsserver per window
VS Code default setup1,451 MBper window: semantic tsserver with automatic type acquisition, plus a syntax server

Time to first diagnostics in the same run: first window 1,252 ms (plain tsserver 856 ms), second window 5 ms (plain tsserver 548 ms).

How we measured
Measured
Total resident memory (RSS) of every process in each setup, summed with their children, after both windows have diagnostics for every open file; one run
Machine
Apple M-series MacBook (macOS arm64, 16 cores, 48 GB), shared with other jobs while measuring; release build, 4 threads
Software
TypeScript 6.0.3 (tRPC's tsserver) as the type backend
Input
tRPC at the commit pinned in bench/repos.toml; window A opens packages/server/src/unstable-core-do-not-import/router.ts and packages/server/src/index.ts, window B packages/client/src/index.ts and packages/client/src/links/httpBatchLink.ts
Command
cargo build --release -p ts_r_lsp --example two_windows && scripts/limit.sh -- target/release/examples/two_windows target/bench-repos/trpc
Recorded in
commit 9c3a371, bench/README.md, crates/ts_r_lsp/examples/two_windows.rs

ts-r-run: running a TypeScript file

Time from starting the command to the program exiting, against tsx and Node's own type stripping. node -e 0 is the floor: starting Node with no TypeScript at all.

Run hello.tsShorter is faster, in ms
  • ts-r
  • Other tools
  • Baseline
  • node -e 0 (floor)23 ms
  • ts-r-run, warm cache29 ms
  • ts-r-run, empty cache32 ms
  • node --experimental-strip-types50 ms
  • tsx 4.2379 ms
Show as table
Run hello.ts (ms, lower is better)
SetupMedian time
node -e 0 (floor)23 ms
ts-r-run, warm cache29 ms
ts-r-run, empty cache32 ms
node --experimental-strip-types50 ms
tsx 4.2379 ms
Run a 1,000-module programShorter is faster, in ms
  • ts-r
  • Other tools
  • Baseline
  • node -e 0 (floor)24 ms
  • ts-r-run, warm cache101 ms
  • ts-r-run, empty cache137 ms
  • node --experimental-strip-types215 ms
  • tsx 4.23270 ms
Show as table
Run a 1,000-module program (ms, lower is better)
SetupMedian time
node -e 0 (floor)24 ms
ts-r-run, warm cache101 ms
ts-r-run, empty cache137 ms
node --experimental-strip-types215 ms
tsx 4.23270 ms
How we measured
Measured
Wall time, median of 15 runs
Machine
Apple M-series Mac, macOS arm64, release build (the exact chip was not recorded)
Software
Node 24.21.0; tsx 4.23 run as its binary (npx tsx adds about 400 ms); ts-r-run with 4 compile threads and source maps on
Input
A generated project of 1,000 TypeScript modules (interfaces, classes, typed functions, .ts imports so Node type stripping can run it too), and hello.ts
Command
cargo build --release -p ts_r_run --bin ts-r-run --example bench && TS_R_RUN_THREADS=4 scripts/limit.sh -- target/release/examples/bench --runs 15
Recorded in
commit e92e486, crates/ts_r_run/AGENTS.md, docs/design/0002-transform-and-codegen.md

Ecosystem: the top 1,000 npm packages

cargo xtask ecosystem runs each stage that applies to a package and compares it with the reference tool. Pass rate is pass / (pass + fail + error); skipped packages have nothing to compare. The check stage imports each package with skipLibCheck off and passes it when ts-r-check prints the same diagnostics (file, line, column, code) as tsc 7; in the 159 packages that fail, most diagnostics differ: 7,540 of the 8,339 compared differ. No package's own test suite passes with ts-r swapped in yet (0 of 4 selected).

Packages passing each stageLonger is better
  • Parse every file99.9%
  • Bundle and run (vs esbuild)92.2%
  • Transform TypeScript (vs tsc)87.5%
  • Check an import of it (vs tsc 7)77.6%
  • Own test suite0%
Show as table
Packages passing each stage (%, higher is better)
SetupPass ratePass / compared
Parse every file99.9%988 / 989
Bundle and run (vs esbuild)92.2%844 / 915
Transform TypeScript (vs tsc)87.5%42 / 48
Check an import of it (vs tsc 7)77.6%550 / 709
Own test suite0%0 / 4
Items inside the packages, and bundle size
StageItems comparedItems failing
parse (files)39,1011
transform (files)1,1566
bundle (entries)1,21777
check (diagnostics, ours and tsc's)8,3397,540
tests (suites)44
Bundle size, 909 packages, minifiedts-r-build 102,531.6 KiBesbuild 99,466.0 KiB (ts-r +3.1%)
How we measured
Measured
Pass, fail, error or skip per package per stage, compared against the reference tool's output
Machine
Not recorded with the result. ts-r's measured runs so far are on the project's Apple M-series development Mac (macOS arm64, release build).
Software
ts-r at commit cfbbe41; esbuild 0.28.2, Node 24.21.0, tsc 7.0.2
Input
The top 1,000 packages of ecosystem/packages.toml (list of 2026-10-14); one run of 546 s
Command
cargo xtask ecosystem (see ecosystem/README.md)
Recorded in
commit b26d73c, ecosystem/STATUS.md, ecosystem/README.md

Parser, binder, cache and launcher

Throughput of the shared engine parts, on public code. These have no comparison bars yet: the side-by-side parser benchmark against Oxc and esbuild is set up in bench/scenarios.toml but ts-r has no parse-only command for it yet.

WhatResultMeasured on
Parse, single threadabout 225 MB/s (30,000 files/s)tRPC including node_modules: 60,419 files, 456.5 MB; 4 files report syntax errors (not yet compared with tsc). Apple M-series shared laptop, 2026-10-16; command in crates/ts_r_parser/AGENTS.md.
Parse and bind, 4 threadsabout 44,600 files/s (279 MB/s); bind alone about 85,000 files/s per threadtRPC: 1,050 project files + 10,887 node_modules declaration files (74.7 MB), 0 panics. Apple M-series shared laptop, 2026-10-16; command in crates/ts_r_binder/AGENTS.md.
Shared cache, warm lookup of 10,000 entries (4 KiB each)27 ms on one thread, 9.5 ms on all corescargo run --release -p ts_r_cache --example bench. Apple M-series. Commit 8d32516.
ts-r launcher overheadabout 0.5 ms (ts-r --version: 2.1 ms)500 runs via Python subprocess, whose own spawn of /usr/bin/true is 1.6 ms median. Apple M-series. Commit 8d32516.

Correctness against tsc 7.0.2, esbuild and ESLint

Speed only counts if the output is right. These compare ts-r with the tool it replaces as the reference. The full list, generated from the crates' notes, is on the Compatibility and status page.

WhatResultMeasured on
Module resolution63,958 of 63,958 resolutions match (0 differences)Every resolution in tsc --traceResolution for 24 tsconfig files of private-app. Commit 856e43d.
tsconfig loading49 of 49 configs match tsc --showConfig exactlyprivate-app tsconfig files. Commit 856e43d.
JavaScript output (emit)339 of 339 files matchprivate-app apps and packages, 15 tsconfig files. Commit e92e486.
JavaScript output (emit)1,611 match, 159 differtsc 7's own emit baselines (tests below ES2022, outFile, AMD/System/UMD and files with syntax errors skipped). Commit b46cb28.
Parse, print, parse again18,079 match, 0 differTypeScript's tests/cases (1,133 files with syntax errors skipped). Commit f3c5476.
Binder diagnostics398 of 442 tests match exactly; 1,683 of 1,789 expected diagnostics foundtsc 7.0.2 baselines for the binder's error codes. Commit 16a6b7e.
Type checker58.0% of 13,398 conformance test variants exact; 937 variants with a diagnostic tsc does not reporttsc 7.0.2 conformance baselines. Commit aaa5051.
Bundler643 of 746 compared cases pass (86.2%)esbuild 0.28.2's end-to-end bundler tests. Commit 7db7af5.
Lint rules8,597 of 8,597 cases matchEvery case of the published ESLint and typescript-eslint tests for the 103 rules. Commit 27c280e.

Not measured yet

The plan compares every tool against the one it replaces. These comparisons have no numbers yet:

The raw data for this page is bench.json. Each tool’s page (Tools) draws the sections that belong to it.