Was tatsächlich passiert ist
TypeScript 7.0 ist kein Feature-Release. Es ist derselbe Compiler, noch einmal geschrieben — in Go statt in TypeScript.
Über ein Jahr lang hat das Team den bestehenden Compiler und Language Service Zeile für Zeile nach Go portiert. Der erklärte Anspruch war Treue statt Neuerfindung: Struktur und Logik der Originalcodebasis blieben erhalten, damit beide Compiler dieselben Ergebnisse liefern. Was sich ändert, ist ausschließlich das Fundament — nativer Maschinencode statt JavaScript, echte Threads mit gemeinsamem Speicher statt Single-Thread-Node.
Praktisch heißt das drei Dinge. Erstens: tsc ist eine native Binary, kein Node-Skript mehr. Zweitens: Der Language Service spricht jetzt LSP, läuft also in jedem modernen Editor und kann mehrere Anfragen parallel bedienen. Drittens — und das ist der teure Teil — es gibt in 7.0 keine stabile Programmier-API. Alles, was TypeScript bisher als Bibliothek eingebunden hat, läuft vorerst weiter gegen 6.0.
In den Vorschauversionen hieß die Binary tsgo und lag im Paket @typescript/native-preview. Seit GA heißt sie wieder tsc und liegt im normalen typescript-Paket. Ältere Blogposts und Stack-Overflow-Antworten reden noch von tsgo — gemeint ist dasselbe Programm.
Die Zahlen
Microsofts eigene Messungen auf öffentlichen Codebases, vollständiger Build, Standardeinstellung --checkers 4:
| Codebase | TS 6 | TS 7 | Faktor | Speicher |
|---|---|---|---|---|
| vscode | 125,7 s | 10,6 s | 11,9× | −18 % |
| sentry | 139,8 s | 15,7 s | 8,9× | −6 % |
| bluesky | 24,3 s | 2,8 s | 8,7× | −26 % |
| playwright | 12,8 s | 1,47 s | 8,7× | −11 % |
| tldraw | 11,2 s | 1,46 s | 7,7× | −15 % |
Bemerkenswert ist die zweite Spalte von rechts: Der Speicherbedarf sinkt, obwohl parallel gearbeitet wird. Mit --checkers 8 steigt der Faktor auf der vscode-Codebase auf 16,7× (7,51 s), allerdings zulasten des Speichers.
Für den Alltag zählt aber weniger der Full Build als der Editor. Im vscode-Repository dauerte es früher rund 17,5 Sekunden vom Öffnen bis zum ersten angezeigten Fehler — jetzt unter 1,3 Sekunden. Microsoft berichtet außerdem von über 80 % weniger fehlgeschlagenen Language-Server-Kommandos und über 60 % weniger Server-Abstürzen gegenüber 6.0.
Alle Zahlen oben stammen vom Hersteller. Unabhängige Berichte aus den ersten Wochen nach GA lagen grob im selben Bereich, aber der ehrliche Satz lautet: Der Faktor auf deiner Codebase hängt von deren Form ab — viele kleine Dateien parallelisieren besser als wenige riesige mit tiefen Typabhängigkeiten.
Installation und Editor
Der Bezugsweg ist unspektakulär — genau das ist der Punkt:
# Stabiles TypeScript 7
npm install -D typescript
npx tsc --version
# Nightlies der 7er-Linie
npm install -D typescript@next
# Einmal ohne Emit durchchecken
npx tsc --noEmitIm Editor läuft alles über LSP. Für VS Code gibt es eine eigene Extension (TypeScriptTeam.native-preview), die sich nach der Installation automatisch als Standard setzt; über die Befehlspalette lässt sie sich mit „Disable TypeScript 7 Language Server" jederzeit wieder abschalten — ein Schalter, den man im Migrationsalltag öfter braucht, als man denkt. Visual Studio aktiviert TypeScript 7 abhängig vom Workspace von selbst, andere Editoren binden es als gewöhnlichen LSP-Server ein.
Die neuen Defaults
TypeScript 7.0 übernimmt die Standardwerte von 6.0. Wer von 5.x kommt, überspringt damit zwei Bruchkanten auf einmal.
Der offizielle Satz lautet: Code, der unter 6.0 mit stableTypeOrdering und ohne ignoreDeprecations sauber kompiliert, kompiliert unter 7.0 identisch. Der Umkehrschluss ist die eigentliche Arbeit — 6.0 zuerst, dann 7.0.
| Option | alt | neu ab 6.0 / 7.0 |
|---|---|---|
| strict | false | true |
| module | commonjs | esnext |
| target | es5 / es3 | aktuelles ES (derzeit es2025) |
| noUncheckedSideEffectImports | false | true |
| libReplacement | true | false |
| stableTypeOrdering | — | true, nicht abschaltbar |
| rootDir | abgeleitet | ./ (Config-Verzeichnis) |
| types | alle @types | [] (leer) |
Die zwei, die wirklich beißen
types ist jetzt leer. Bisher lud TypeScript stillschweigend jedes Paket unter node_modules/@types in den globalen Scope. In größeren Monorepos sind das schnell mehrere hundert Deklarationsdateien. Ab sofort lädt es keines mehr — bis du sie aufzählst. Das Symptom ist unmissverständlich: plötzlich kennt niemand mehr process, describe oder Bun.
{
"compilerOptions": {
+ "types": ["node", "jest"]
// ["*"] stellt das alte Verhalten wieder her – aber genau das kostete Zeit
}
}Microsoft berichtet, dass allein das explizite Setzen von types die Buildzeit vieler Projekte um 20 bis 50 Prozent verbessert hat. Der Default ist also kein Ärgernis, sondern das größte kostenlose Performance-Geschenk der Migration.
rootDir ist jetzt das Config-Verzeichnis. Früher wurde das gemeinsame Wurzelverzeichnis aus allen Eingabedateien abgeleitet. Jetzt ist es schlicht der Ordner, in dem die tsconfig.json liegt. Das Symptom: Deine Ausgabe landet in dist/src/index.js statt in dist/index.js.
{
"compilerOptions": {
"outDir": "./dist",
+ "rootDir": "./src"
},
"include": ["./src"]
}stableTypeOrdering
Diese Option verdient eine eigene Erklärung, weil sie erklärt, warum die Parallelisierung überhaupt funktionieren kann. TypeScript vergab interne Typ-IDs bisher in der Reihenfolge, in der Typen dem Compiler begegneten — und sortierte Unions danach. Eine unbeteiligte const weiter oben in der Datei konnte den emittierten Union-Typ von 100 | 500 zu 500 | 100 drehen.
Bei parallelem Type-Checking wäre diese Reihenfolge nicht mehr deterministisch: Verschiedene Checker sehen verschiedene Reihenfolgen, und identische Dateien könnten unterschiedliche Deklarationsdateien oder sogar unterschiedliche Fehler produzieren. TypeScript 7 sortiert deshalb inhaltsbasiert und deterministisch. In 6.0 kann man dieses Verhalten mit --stableTypeOrdering vorab einschalten — kostet dort bis zu 25 % Checkzeit, ist aber während der Migration die ehrlichste Vergleichsbasis.
Ein neuer Fehler unter stableTypeOrdering bedeutet fast immer: Die Inferenz hat vorher zufällig funktioniert, weil die Reihenfolge günstig war. Das Gegenmittel ist eine explizite Annotation — ein Typargument am Aufruf oder eine Typannotation an der Variablen, die hineingereicht wird.
Entfernt: die harten Fehler
Was in 6.0 eine Deprecation-Warnung war, ist in 7.0 ein Fehler ohne Ausweichflag. ignoreDeprecations hilft hier nicht mehr.
amd-module-Direktive wirkt nicht mehr.paths und Suchwurzel für Modulauflösung — die zweite Rolle war meist unbeabsichtigt. Präfix in die paths-Einträge ziehen.allowSyntheticDefaultImports: false
import * as express wird zu import express.await, static, private oder public müssen umbenannt werden.module-Schlüsselwort für Namespaces ist ein Fehler — namespace Foo { } verwenden. declare module "x" bleibt unverändert gültig.with { type: "json" } verwenden.--noLib oder --libReplacement.--ignoreConfig quittieren.{
"compilerOptions": {
- "baseUrl": "./src",
"paths": {
- "@app/*": ["app/*"],
- "@lib/*": ["lib/*"]
+ "@app/*": ["./src/app/*"],
+ "@lib/*": ["./src/lib/*"]
}
}
}Falls baseUrl tatsächlich als Suchwurzel diente — selten, aber es kommt vor — lässt sich das mit einem Catch-all "*": ["./src/*"] nachbilden. Für die mechanischen Anteile dieser Umstellung gibt es das experimentelle Werkzeug ts5to6, das baseUrl und rootDir automatisch anpassen kann.
Fix-Generator
Kreuze an, was auf dein Projekt zutrifft. Unten entsteht die passende tsconfig.json plus die Punkte, die sich nicht durch Konfiguration lösen lassen.
{
"compilerOptions": {
// Nichts ausgewählt – die Defaults von 7.0 reichen.
}
}
Der Generator deckt Konfiguration ab, nicht Semantik. Was danach an Typfehlern übrig bleibt, ist echte Arbeit an deinem Code — vor allem, wenn strict neu dazukommt.
Parallelisierung steuern
Parsing und Emit lassen sich sauber über Dateien verteilen. Type-Checking nicht: Fast jede Datei zieht Typinformationen aus ihren Abhängigkeiten und dem globalen Scope. TypeScript 7 löst das mit einer festen Anzahl von Checker-Workern, die jeweils ihre eigene Sicht auf das Programm haben, sich die Dateien bei gleichem Input immer identisch aufteilen — und deshalb reproduzierbar dieselben Ergebnisse liefern, auch wenn sie manche Arbeit doppelt machen.
# Standard: 4 Checker
tsc
# Mehr Kerne, mehr Tempo, mehr Speicher
tsc --checkers 8
# CI-Runner mit wenig RAM: keine Doppelarbeit
tsc --checkers 1
# Monorepo: mehrere Projekte gleichzeitig bauen
tsc --build --checkers 4 --builders 4 # bis zu 16 Checker gleichzeitig!
# Alles seriell – zum Debuggen und Vergleichen
tsc --singleThreaded| Flag | Wirkung | Kosten |
|---|---|---|
| --checkers n | Type-Checker-Worker (Default 4) | Speicher, Doppelarbeit |
| --builders n | parallele Projekte unter --build | multipliziert mit --checkers |
| --singleThreaded | alles seriell, auch Parsing und Emit | volle Geschwindigkeit weg |
In seltenen Fällen kann eine unterschiedliche Anzahl Checker reihenfolgeabhängige Ergebnisse sichtbar machen. Deshalb: eine feste Zahl über alle Umgebungen hinweg festlegen — lokal, CI und Release-Build sollten denselben Wert verwenden. --builders ist davon nicht betroffen.
Watch-Modus
Der Watch-Modus wurde komplett neu gebaut. Go hat keine Dateiüberwachung in der Standardbibliothek, und die verfügbaren Bibliotheken überzeugten nicht — also portierte das Team den Watcher des Parcel-Bundlers von C++ nach Go, mit einigen Assembly-Shims, um keine C++-Toolchain in den Buildprozess zu holen. Wer tsc --watch auf einem großen Repository mit vollem node_modules laufen lässt, merkt den Unterschied vor allem am Leerlaufverbrauch.
Änderungen an Typsystem und JS-Support
Template-Literal-Typen respektieren Codepoints
Bisher folgte die Inferenz in Template-Literal-Typen der UTF-16-Indizierung von JavaScript und zerlegte Emoji in ihre Surrogatpaare. Das war formal konsistent, aber praktisch fast nie gewollt.
type HeadTail<S> = S extends `${infer Head}${infer Tail}` ? [Head, Tail] : never;
type R = HeadTail<"😀abc">;
// TS 7: ["😀", "abc"]
// TS 6: ["\ud83d", "\ude00abc"]Das Verhalten entspricht jetzt dem, was for…of oder [...str] zur Laufzeit tun. Für die allermeisten Projekte ist das eine Korrektur. Wer allerdings auf Typebene absichtlich UTF-16-Einheiten gezählt hat — etwa in einem generischen Length<S>-Helfer — bekommt hier andere Ergebnisse.
JavaScript und JSDoc
Beim Portieren hat das Team die Sonderbehandlung von .js-Dateien aufgeräumt. Vieles davon war über Jahre gewachsen und an Closure-Konventionen orientiert; jetzt wird JavaScript weitgehend so analysiert wie TypeScript. Was das konkret bedeutet:
typeof someValue schreiben.@typedef auf (typeof MeinEnum)[keyof typeof MeinEnum].any verwenden.class-Deklaration verwenden.T schreiben./** @typedef {T} Name */, nicht daneben.function(string): void entfällt. Ersatz: (s: string) => void.var self = this oder das Ersetzen des kompletten prototype werden nicht mehr besonders behandelt.Diese Liste ist in Bewegung. Das Team pflegt die vollständigen Unterschiede zwischen 6.0 und 7.0 in der Datei CHANGES.md im typescript-go-Repository — die verlässlichste Quelle, wenn ein Fehler auftaucht, der in keinem Blogpost steht.
Das Ökosystem — hier liegt die eigentliche Arbeit
Der Compiler ist stabil. Was in den ersten Wochen nach GA Probleme machte, war alles andere.
Der Grund ist ein einziger: TypeScript 7.0 liefert keine stabile Programmier-API. Jedes Werkzeug, das TypeScript nicht als Kommandozeile aufruft, sondern als Bibliothek einbindet — Linter, Testrunner, Bundler-Loader, Template-Checker — greift ins Leere. Die neue API ist für 7.1 angekündigt.
typescript-estree.Das Side-by-Side-Muster
Microsoft hat für genau diesen Übergang das Paket @typescript/typescript6 veröffentlicht. Es bringt eine Binary namens tsc6 mit und re-exportiert die vollständige 6.0-API. Weil Werkzeuge wie typescript-eslint per Peer-Dependency direkt aus typescript importieren, führt der Weg über npm-Aliase:
{
"devDependencies": {
// tsc -> TypeScript 7 (schnell, für Check und Build)
"@typescript/native": "npm:typescript@^7.0.2",
// tsc6 -> TypeScript 6 (API-Oberfläche für Linter, Jest, Volar)
"typescript": "npm:@typescript/typescript6@^6.0.2"
}
}{
"scripts": {
"typecheck": "tsc --noEmit", // 7.0, schnell
"typecheck:legacy": "tsc6 --noEmit", // 6.0, Referenz
"build": "tsc6 -p tsconfig.build.json",
"lint": "eslint ." // nutzt weiter 6.0
}
}Zwei Compiler, zwei Aufgaben. TypeScript 7 ist der schnelle Prüfer — im Editor und als nicht-blockierender Schritt in CI. TypeScript 6 bleibt die Quelle der Wahrheit für Emit und für alles, was die API braucht. Sobald 7.1 mit stabiler API da ist, fällt die zweite Hälfte weg.
Migration in Phasen
Die Reihenfolge ist wichtiger als das Tempo. Wer direkt von 5.x auf 7.0 springt, bekommt die Brüche von zwei Major-Versionen gleichzeitig auf den Tisch und kann nicht mehr unterscheiden, welcher Fehler woher kommt.
- Phase 0 — Grundlage schaffen
- Auf TypeScript 6.0 aktualisieren, noch mit
"ignoreDeprecations": "6.0" - Baseline messen:
tsc --noEmitmit Zeitmessung, Fehlerzahl notieren - Alle Deprecation-Warnungen einsammeln und als Tickets ablegen
- Phase 1 — Konfiguration bereinigen
typesexplizit setzen — meist["node"]plus TestrunnerrootDirexplizit setzen, Ausgabepfade indist/prüfenbaseUrlentfernen, Präfixe in diepaths-Einträge ziehenmoduleResolutionaufnodenextoderbundlerhebentarget,module,outFile,downlevelIterationabräumenignoreDeprecationsentfernen — ab hier muss 6.0 sauber sein- Phase 2 — strict und Reihenfolge
strict: trueaktivieren, notfalls modulweise über Project References--stableTypeOrderingeinschalten und die neuen Fehler abarbeiten- Emittierte
.d.tszwischen 6.0 und 7.0 vergleichen - Phase 3 — 7.0 daneben stellen
- TypeScript 7 per npm-Alias parallel installieren, 6.0 unangetastet lassen
- Nicht-blockierenden CI-Job mit
tsc --noEmitergänzen --checkersfestlegen und über alle Umgebungen gleich halten- Team-Editoren auf den TS-7-Language-Server umstellen (außer bei Volar-Stacks)
- Phase 4 — umschalten
- CI-Job auf blockierend stellen, sobald die Fehlerlisten deckungsgleich sind
- Emit auf 7.0 umstellen — nur wenn kein API-Werkzeug in der Build-Kette hängt
- 7.1 im Blick behalten und das 6.0-Alias entfernen, sobald die API da ist
Der Haken wird nicht gespeichert — die Liste ist zum Durcharbeiten in einer Sitzung gedacht, nicht als Projektmanagement.
Was als Nächstes kommt
Mit 7.0 ist der Port abgeschlossen, und das Team kehrt zur normalen Arbeit zurück: neue Sprachfeatures, Ergonomie, weitere Performance — und die neue API. Die Kadenz soll wieder bei einem Release alle drei bis vier Monate liegen.
Die wichtigste Zahl für die Planung ist deshalb nicht 8× oder 12×, sondern 7.1. Bis dahin ist TypeScript 7 ein hervorragender Type-Checker und Editor-Motor mit einem Loch in der Werkzeugkette. Danach ist es einfach TypeScript.
Erst 6.0 sauber machen. Dann 7.0 daneben stellen, nicht darüber. Emit zuletzt umstellen. Und die types-Zeile nicht vergessen — sie ist die billigste Beschleunigung der ganzen Migration.