# tsrs vs tsgo: Cloudflare sagt 8× – warum steht im Benchmark 16,87×?

> Canonical: https://vxi.me/de/posts/tsrs-vs-tsgo-typescript-rust-compiler/
> Locale: de-de
> Published: 2026-10-22
> Updated: 2026-10-22
> Markdown: https://vxi.me/de/posts/tsrs-vs-tsgo-typescript-rust-compiler.md

<figure class="tsrs-parity-hero not-prose">
<picture><source media="(prefers-reduced-motion: reduce) and (max-width: 640px)" srcset="/images/posts/tsrs-vs-tsgo-typescript-rust-compiler/tsrs-diagnostic-parity.mobile.still.de-de.svg" type="image/svg+xml"><source media="(prefers-reduced-motion: reduce)" srcset="/images/posts/tsrs-vs-tsgo-typescript-rust-compiler/tsrs-diagnostic-parity.still.de-de.svg" type="image/svg+xml"><source media="(max-width: 640px)" srcset="/images/posts/tsrs-vs-tsgo-typescript-rust-compiler/tsrs-diagnostic-parity.mobile.de-de.svg" type="image/svg+xml"><img src="/images/posts/tsrs-vs-tsgo-typescript-rust-compiler/tsrs-diagnostic-parity.de-de.svg" alt="tsgo und tsrs führen vier synthetische TypeScript-Testfälle mit derselben Quellrevision aus; Diagnosen und Exitcodes stimmen in allen vier Fällen überein." width="1200" height="700" loading="eager" fetchpriority="high" decoding="async" style="display:block;width:100%;height:auto"></picture>
<figcaption>Abbildung: Diagnosevergleich für vier synthetische Einzeldatei-Fixtures. Alle vier Ausgaben stimmen überein; dies ist kein Projekt-Kompatibilitäts- oder Performance-Benchmark.</figcaption>
</figure>

Cloudflare stellte tsrs mit einer auffälligen Zahl vor: bis zu achtmal schneller und rund 70 Prozent weniger Speicher als TypeScript 7.0. In einem auf einen bestimmten Commit festgelegten tsrs-Bericht fand ich aber 16,87× für mui-docs und 13,55× für vscode in der Standardkonfiguration. Wie passt das zur Aussage mit 8×?

Die großen Performance-Benchmarks habe ich nicht selbst wiederholt. Ich habe stattdessen eine kleinere Frage überprüft: Die zueinander passenden Versionen von tsrs und tsgo (TypeScript in Go) liefen in einer temporären, isolierten GitHub-Umgebung gegen vier TypeScript-Beispiele. Die Diagnosen stimmten in allen vier Fällen überein. Das rechtfertigt weitere Tests, aber nicht den sofortigen Ersatz der Typprüfung in der produktiven CI.

## Warum die Zahl der Checker im Benchmark zählt

Cloudflares [Ankündigung](https://blog.cloudflare.com/typescript-rust-port/) fasst frühe Ergebnisse als bis zu 8× schneller und rund 70 Prozent weniger Speicher zusammen. Der fest gepinnte [historische Benchmarkbericht](https://github.com/oxc-project/tsrs/blob/357d1d3ec23180eae94a8485b8b7444487a7ea5f/bench/RESULTS.md) von tsrs führt dagegen die einzelnen Messwerte auf:

| Historischer Upstream-Test | tsgo | tsrs | Checker | Faktor laut Bericht |
| --- | ---: | ---: | --- | ---: |
| mui-docs, Standard | 12.66 s | 0.75 s | 4 vs 16 | 16.87× |
| mui-docs, beide `--checkers 4` | 12.69 s | 1.07 s | 4 vs 4 | 11.81× |
| mui-docs, beide `--checkers 16` | 12.36 s | 0.77 s | 16 vs 16 | 16.07× |
| vscode, Standard | 11.10 s | 0.82 s | 4 vs 16 | 13.55× |
| vscode, beide `--checkers 16` | 6.73 s | 0.81 s | 16 vs 16 | 8.32× |

Die Standardzeilen verwenden nicht dieselbe Anzahl Checker: tsgo vier, tsrs sechzehn. Selbst mit gleicher Anzahl – vier oder sechzehn – liegen die Faktoren in der Tabelle über acht. Das sind zwei unterschiedliche Aussagen: Der Beitrag nennt einen frühen zusammengefassten Messwert; `RESULTS.md` ist ein separater historischer Snapshot mit festen Versionen und Bedingungen. Acht ist keine obere Grenze für diese Tabelle, und sechzehn ist kein Leistungsversprechen für das eigene Projekt.

Warum die Abweichung so groß ist, kann ich nicht aus den Zahlen allein erklären. Im Bericht steht eine Depot-CI-Maschine `depot-ubuntu-24.04-16` mit 16 vCPUs, 63 GB RAM und Linux x86_64. Laufzeiten und maximaler Speicher wurden in wechselnder Reihenfolge gemessen und als Median ausgewertet. tsgo verwendet `typescript@7.0.2`, tsrs basiert auf einem neueren TypeScript-7.1-dev-Commit und wurde als PGO-optimierter Release-Build erstellt. Gleiche Checker-Anzahlen bedeuten deshalb noch keine identischen Ausgangsbedingungen.

Auch die Speichermessungen hängen von Projekt und Einstellung ab. Bei mui-docs gibt der Bericht unter den Standards einen Faktor von 4,61× weniger Speicher an, bei sechzehn Checkern auf beiden Seiten 8,23× weniger. Die „rund 70 Prozent“ aus der Ankündigung sind ein Überblick über frühe Ergebnisse, keine Garantie für alle Projekte. Ich habe dazu den Upstream-Bericht geprüft, keine eigenen großen Benchmarks gerechnet.

## Was die vier Diagnose-Tests wirklich abdecken

Verglichen wurden `tsrs@0.18.0-ts7.1.0-dev.20260929` und die Go-Implementierung aus `typescript@7.1.0-dev.20260930.4`. Beide beziehen sich auf denselben [TypeScript-Quellcommit b85298b6](https://github.com/microsoft/TypeScript/commit/b85298b6a81f772d080b0455de0ca9d744cd6fd6).

Die Vorbereitung begann mit einem handfesten Fehler: Ich behandelte `@typescript/typescript`, den Workspace-Namen im TypeScript-Quellrepository, wie ein bei npm veröffentlichtes Paket. npm antwortete mit E404 – der Compiler lief noch gar nicht. Das veröffentlichte Root-Paket heißt `typescript`. In diesem Nightly heißt die CLI noch `tsc`, und `@typescript/typescript-linux-x64` stellt die native Go-Version für Linux x64 bereit. Erst nach der Korrektur begann die Kompilierung.

Alle vier Fixtures nutzen dieselbe `tsconfig.json`. Ich setze `types: []` explizit, damit globale `@types`-Pakete aus der Umgebung nicht heimlich hinzukommen; `skipLibCheck: false` lässt die Prüfung von Deklarationsdateien eingeschaltet. Das ist die verwendete Konfiguration, statt nur von „strict mode“ zu sprechen:

~~~json
{
  "compilerOptions": {
    "strict": true,
    "target": "es2022",
    "module": "esnext",
    "noEmit": true,
    "skipLibCheck": false,
    "types": []
  },
  "include": ["src/*.ts"]
}
~~~

In jedem Fixture-Verzeichnis liegt genau eine `src/test.ts` mit folgendem Inhalt:

| Fixture | Vollständige `src/test.ts` | Ergebnis tsgo / tsrs |
| --- | --- | --- |
| `valid` (Gültige Typen) | `export const answer: number = 42;` | 0 Diagnosen, Exit 0 / 0 |
| `type-error` (Falsche Typzuweisung) | `export const answer: number = "wrong";` | TS2322 · Exit 2 / 2 |
| `module-missing` (Fehlendes Modul) | `import {missing} from "./unavailable.js"; export const answer = missing;` | TS2307 · Exit 2 / 2 |
| `generic-constraint` (Generische Einschränkung) | `type OnlyText<T extends string> = {value:T}; export type Bad = OnlyText<number>;` | TS2344 · Exit 2 / 2 |

Beide Seiten wurden mit `--noEmit --pretty false --checkers 4` gestartet. Ich verglich Dateien, Zeilen- und Spaltenpositionen, Fehlercodes, Texte, Reihenfolge und Prozess-Exitcodes. Alle vier Fälle stimmten überein. Da jedes Fixture nur eine Quelldatei besitzt, ist die Option `--checkers 4` hier keine Messung echter Vierfachparallelität.

## Gleiche Fehleranzahl bedeutet nicht gleiche Diagnose

Die [Kompatibilitätsangaben](https://github.com/oxc-project/tsrs/blob/357d1d3ec23180eae94a8485b8b7444487a7ea5f/docs/COMPATIBILITY.md) zur fixierten Version melden 13.458 übereinstimmende Fehler-Baselines von 13.462 sowie alle 12.779 `.types`- und `.symbols`-Baselines. Gleiche Summen allein reichen aber nicht.

Im Upstream-Bericht ist `drizzle-orm` ein gutes Gegenbeispiel: Mit sechzehn Checkern auf beiden Seiten wurden jeweils 10.846 Fehler gemeldet, deren Positionen jedoch unterschiedlich waren. Der Bericht bezeichnet das als tsrs-Bug. Diesen Fall habe ich nicht selbst reproduziert, sondern in der fixierten Quelle nachgelesen. Für CI und Code-Reviews muss ein Fehler auf die richtige Datei und Zeile zeigen.

Es gibt weitere Versionsgrenzen: `--init` und `--showConfig` sind noch nicht unterstützt, `--locale` übersetzt keine Diagnosen. Die Content-Mapper für `.astro`, `.vue` und `.svelte` funktionieren für CLI/Build, nicht aber für den Language Server. Vorgefertigte Pakete für Windows oder Intel-macOS fehlen ebenfalls. Mein Versuch umfasste nur die CLI in einer isolierten GitHub-Linux-Umgebung; IDE-Plugins, LSP und diese Systeme habe ich nicht getestet. Wer mit `astro check` oder Live-Diagnosen arbeitet, sollte zuerst die [Kompatibilitätstabelle](https://github.com/oxc-project/tsrs/blob/357d1d3ec23180eae94a8485b8b7444487a7ea5f/docs/COMPATIBILITY.md) der passenden Version lesen. Bei Problemen nach einem Astro-Upgrade gibt es von mir auch eine [Notiz zur TypeScript-7-Kompatibilität](/posts/astro-check-typescript-7-compatibility/).

## Was ich im eigenen Projekt vergleichen würde

Ich würde in einem wegwerfbaren Git-Worktree oder separaten Branch anfangen, nicht im täglichen Arbeitsverzeichnis. So lässt sich ein erfolgloser Test einfach verwerfen.

Das nächste Skript verhindert eine leicht zu übersehende Verwechslung: Gleiche Exitcodes bedeuten nicht automatisch gleiche Diagnosetexte. `set +e` ist Absicht. Die bewusst falschen Beispiele sollen mit Nicht-Null-Status enden; wir müssen beide Programme durchlaufen lassen, Exitcodes sichern und anschließend Ausgaben vergleichen. `tsc` ist hier die CLI der Go-Implementierung im fixierten TypeScript-Nightly, `/tmp/tsgo.out` nur der Protokolldateiname.

~~~bash
pnpm add -D --save-exact tsrs@0.18.0-ts7.1.0-dev.20260929 typescript@7.1.0-dev.20260930.4

set +e
pnpm exec tsc -p . --noEmit --pretty false --checkers 4 > /tmp/tsgo.out 2>&1
tsgo_status=$?
pnpm exec tsrs -p . --noEmit --pretty false --checkers 4 > /tmp/tsrs.out 2>&1
tsrs_status=$?
diff -u /tmp/tsgo.out /tmp/tsrs.out
diff_status=$?
set -e
printf 'tsgo_exit=%s tsrs_exit=%s diff_exit=%s\n' "$tsgo_status" "$tsrs_status" "$diff_status"
if [ "$tsgo_status" -ne "$tsrs_status" ] || [ "$diff_status" -ne 0 ]; then
  echo 'DIAGNOSTIC_DIFF_OR_EXIT_MISMATCH: inspect /tmp/tsgo.out and /tmp/tsrs.out'
  exit 1
fi
~~~

Wenn der erste Vergleich abweicht, bewahre ich die Originalausgaben auf. Dann lassen sich `--checkerAssignment go` (Dateiverteilung und Checker-Historie wie bei tsgo) oder `--noLazyMembers` (ohne die lazy-member-Optimierung von tsrs) separat ausprobieren. Wer sie schon beim ersten Lauf aktiviert, sieht das abweichende Standardverhalten möglicherweise nicht mehr. Weitere Details: [Anleitung](https://github.com/oxc-project/tsrs/blob/357d1d3ec23180eae94a8485b8b7444487a7ea5f/docs/USAGE.md) und [Standardwert-Unterschiede](https://github.com/oxc-project/tsrs/blob/357d1d3ec23180eae94a8485b8b7444487a7ea5f/docs/DEFAULTS.md).

Das Skript oben misst weder Laufzeit noch Speicher. Eine vollständige Performance-Messreihe habe ich diesmal nicht durchgeführt. Für einen ersten Einzelwert lässt sich GNU `time` unter Linux verwenden, um Sekunden und Peak RSS zu erfassen. Unter macOS kann man `gnu-time` installieren und die erste Zeile auf `TIME_BIN=gtime` ändern.

~~~bash
TIME_BIN=/usr/bin/time

set +e
"$TIME_BIN" -f 'elapsed_s=%e max_rss=%M' -o /tmp/tsgo-bench.metrics \
  pnpm exec tsc -p . --noEmit --pretty false --checkers 4 \
  > /tmp/tsgo-bench.out 2>&1
tsgo_status=$?
"$TIME_BIN" -f 'elapsed_s=%e max_rss=%M' -o /tmp/tsrs-bench.metrics \
  pnpm exec tsrs -p . --noEmit --pretty false --checkers 4 \
  > /tmp/tsrs-bench.out 2>&1
tsrs_status=$?
set -e

printf 'tsgo_exit=%s ' "$tsgo_status"; cat /tmp/tsgo-bench.metrics
printf 'tsrs_exit=%s ' "$tsrs_status"; cat /tmp/tsrs-bench.metrics
~~~

Aus diesen Kommandos entsteht nur ein Messpaar; Hintergrundlast kann es stark beeinflussen. Für eine belastbarere Aussage würde ich auf demselben Runner mindestens fünf Paare in wechselnder Reihenfolge ausführen, Lockfile, Compiler-Versionen, `tsconfig` und Checker-Anzahl festhalten und Mediane samt Peak RSS vergleichen. Kaltstart und Warm-Cache-Läufe sollten getrennt dokumentiert werden. Den Betriebssystem-Page-Cache habe ich nicht geleert und würde das deshalb nicht als vollständigen Kaltcache-Test bezeichnen. GNU `time` zeigt `%M` gewöhnlich in KiB an – plattformübergreifend müssen die Einheiten zuerst geprüft werden.

Ich würde tsrs zunächst nur als zusätzlichen, nicht blockierenden CI-Job laufen lassen. Das bisherige `tsc` und die Framework-Prüfung bleiben Pflicht. Erst wenn Diagnoseunterschiede geklärt sind, die Performance-Messungen stabil werden und ein echter PR-/Release-Zyklus samt Rollback erfolgreich nachvollzogen wurde, lohnt die Debatte über einen Wechsel. Vier kleine Beispiele beweisen nur, dass beide Compiler bei diesen Eingaben dieselben Diagnosen liefern.

Versuchsdatum: 22. Oktober 2026
