Zum Inhalt springen
CASOON

TypeScript 6: Das Vorbereitungs-Release

Was sich ändert, warum Defaults strenger werden – und was das mit TypeScript 7 und dem Go-Compiler zu tun hat

Aktualisiert 3. April 2026
12 Minuten
TypeScript 6: Das Vorbereitungs-Release
#TypeScript #TypeScript 6 #JavaScript #Compiler

TypeScript 6.0 ist kein normales Feature-Release. Es ist ein Übergangs-Release – intern als „Bridge Release” bezeichnet – das den Weg zu TypeScript 7.0 ebnet. Wer das nicht weiß, wird sich über einige der Änderungen wundern: strengere Defaults, neue Flags, ein anderer Konfigurationsansatz. Das Ziel ist nicht, den Status quo zu verbessern. Es ist, eine saubere Basis für eine Architekturänderung zu schaffen, die alles verändert.

Warum TypeScript 6.0 kein Spektakel-Release ist

TypeScript 6.0 wirkt auf den ersten Blick unspektakulär. Kein einzelnes Killer-Feature, keine große neue Syntax, kein Release, das sofort alle Entwickler aufgeregt auf X oder Hacker News diskutieren. Genau das kann leicht darüber hinwegtäuschen, wie wichtig diese Version tatsächlich ist.

Denn kleine Releases sind oft dort entscheidend, wo große Feature-Releases schwach bleiben: beim Aufräumen. TypeScript 6.0 räumt technische Schulden im Unterbau auf, verschiebt Defaults in eine modernere Richtung und bereitet die Bedingungen dafür vor, dass TypeScript 7.0 nicht nur schnell, sondern auch stabil in reale Codebasen passt.

Das ist kein Show-Release. Es ist ein Fundament-Release. Und solche Versionen sind im Alltag oft wertvoller als laute Features, die zwar gut klingen, aber wenig an der Realität großer Projekte ändern.

Warum TypeScript 6 anders ist: Project Corsa

Der aktuelle TypeScript-Compiler ist komplett in JavaScript geschrieben – das ist historisch gewachsen und hat lange gut funktioniert. Aber JavaScript ist Single-threaded. Das ist die grundlegende Bremse.

TypeScript 7.0 ersetzt diesen Compiler durch eine neue Implementierung in Go, Codename „Project Corsa”. Go ist nativ multithreaded, kompiliert zu nativen Binaries und hat deutlich besseres Memory-Management als Node.js. Das Ergebnis in frühen Benchmarks: Builds die bisher 30 Sekunden dauerten, laufen in unter 3 Sekunden.

Der Unterschied für verschiedene Projektgrößen:

ProjektgrößeTS 5.x (geschätzt)TS 7.0 (geschätzt)
Kleines Projekt (100 Dateien)~2s~0.3s
Mittleres Projekt (1.000 Dateien)~15s~1.5s
Großes Monorepo (10.000 Dateien)~90s~8s

Das ist kein gradueller Fortschritt – das ist ein anderes Werkzeug.

Warum braucht es dann TypeScript 6.0? Weil Parallelisierung Bedingungen stellt. Der neue Compiler verarbeitet Dateien nicht mehr der Reihe nach, sondern gleichzeitig. Und das bricht Codebases, die implizit auf Verarbeitungsreihenfolge angewiesen sind.

Das wichtigste neue Feature: --stableTypeOrdering

Wenn Dateien parallel verarbeitet werden, ändert sich die Reihenfolge, in der Typen aufgelöst werden. In den meisten Codebases ist das kein Problem – Typen sollten unabhängig von der Reihenfolge konsistent sein. In der Praxis sind sie es aber manchmal nicht.

Das klassische Problem: Circular Dependencies, bei denen der aufgelöste Typ davon abhängt, welche Datei zuerst verarbeitet wird.

// types/user.ts
import { Permission } from './permission';

export type User = {
  id: string;
  permissions: Permission[];
};

// types/permission.ts
import { User } from './user';  // Circular!

export type Permission = {
  action: string;
  grantedTo?: User;  // Typ kann je nach Reihenfolge anders aufgelöst werden
};

Im aktuellen Compiler läuft das deterministisch durch – immer in derselben Reihenfolge. Im neuen Go-Compiler kann User oder Permission zuerst kommen. --stableTypeOrdering aktiviert eine Prüfung, die solche Reihenfolge-Abhängigkeiten jetzt schon findet.

# In tsconfig.json aktivieren
{
  "compilerOptions": {
    "stableTypeOrdering": true
  }
}

Wer diesen Flag aktiviert und keine neuen Fehler sieht, ist bereit für TS 7.0. Wer Fehler sieht, hat echte Probleme in der Codebase – die vorher zufällig funktioniert haben.

Neue Defaults: Was sich ohne Konfigurationsänderung ändert

TypeScript 6.0 dreht mehrere Defaults um. Das betrifft jeden, der von TS 5.x migriert.

strict: true ist jetzt Standard

strict ist kein einzelner Flag – es ist eine Gruppe:

  • strictNullChecks – kein implizites null | undefined mehr
  • strictFunctionTypes – konsistentes Funktions-Typ-Checking
  • strictBindCallApply – korrekte Typen für .bind(), .call(), .apply()
  • strictPropertyInitialization – alle Klassen-Properties müssen initialisiert sein
  • noImplicitAny – kein implizites any mehr
  • noImplicitThis – kein ungetyptes this
  • useUnknownInCatchVariables – catch-Variablen sind unknown, nicht any

Was das in der Praxis bedeutet:

// Vorher (TS 5.x ohne strict): funktioniert
function getUser(id) {  // implizites any für id
  return users[id];
}

// Nachher (TS 6.0): Fehler
function getUser(id) {
//              ^^^ Parameter 'id' implicitly has an 'any' type.
  return users[id];
}

// Fix:
function getUser(id: string) {
  return users[id];
}
// Vorher: null-Zugriff unbemerkt
function getEmail(user: User | null) {
  return user.email;  // kein Fehler, obwohl user null sein kann
}

// Nachher: Fehler
function getEmail(user: User | null) {
  return user.email;
//       ^^^^ Object is possibly 'null'.
}

// Fix:
function getEmail(user: User | null) {
  return user?.email;
}

Für bestehende Projekte bedeutet das: beim Upgrade auf TS 6.0 werden Fehler auftauchen, die vorher still ignoriert wurden. Das ist unbequem – aber es sind echte Fehler.

module: esnext und target: ES2025

Der Compiler geht jetzt davon aus, dass moderne Environments vorliegen. Für neue Projekte ist das die richtige Entscheidung: weniger Konfiguration, moderneres Output.

Für bestehende Projekte, die ältere Browser oder Node.js-Versionen unterstützen müssen, ist eine explizite Konfiguration nötig:

{
  "compilerOptions": {
    "target": "ES2020",
    "module": "commonjs",
    "lib": ["ES2020"]
  }
}

Neue Sprach-Features mit Codebeispielen

Temporal API

Date ist eines der meistgehassten APIs in JavaScript – aus gutem Grund. Mutierbar, timezoneunfreundlich, inkonsistente Methoden. Die Temporal API ist der offizielle Ersatz, der nach Jahren in Stage 3 endlich breite Unterstützung bekommt. TypeScript 6.0 liefert vollständige Typen.

Der eigentliche Punkt dabei ist größer als ein neues API-Objekt. Zeit- und Datumslogik ist in JavaScript seit Jahren ein strukturelles Problem: lokale Zeitzonen schleichen sich implizit ein, Sommerzeitgrenzen erzeugen schwer erklärbare Fehler, und Date vermischt Zeitpunkte, Kalenderdaten und Zeitzonen in einer API, die für fast jede ernsthafte Anwendung zu unpräzise ist.

Temporal trennt diese Konzepte sauberer:

  • PlainDate für Kalenderdaten ohne Zeitzone
  • Instant für exakte Zeitpunkte
  • ZonedDateTime für echte Zeitzonenlogik
  • immutable Operationen statt versteckter Mutation

Das macht Datumslogik nicht magisch einfach, aber deutlich ehrlicher. Und genau deshalb ist die vollständige Typunterstützung in TypeScript 6.0 relevanter, als sie auf den ersten Blick wirkt.

// Alt: Date API – fehleranfällig
const now = new Date();
const tomorrow = new Date(now);
tomorrow.setDate(tomorrow.getDate() + 1);  // Mutation!

// Neu: Temporal API – immutable
const today = Temporal.PlainDate.today('europe/berlin');
const tomorrow = today.add({ days: 1 });  // neues Objekt

// Zeitzonenberechnung
const meeting = Temporal.ZonedDateTime.from({
  timeZone: 'Europe/Berlin',
  year: 2026, month: 3, day: 15,
  hour: 14, minute: 0,
});

const inNewYork = meeting.withTimeZone('America/New_York');
console.log(inNewYork.toString());  // Typgeprüft, keine Überraschungen
// Dauer-Berechnungen
const start = Temporal.PlainDate.from('2026-01-01');
const end = Temporal.PlainDate.from('2026-03-29');
const duration = start.until(end);
console.log(duration.days);  // 87 – korrekt, ohne Millisekunden-Rechnung

Promise.try

Eines der kleinsten, aber praktischsten Additions. Das Problem: wenn synchrone und asynchrone Funktionen gemischt werden, muss der Error-Handling-Code beide Fälle abdecken.

// Alt: try/catch + Promise-Handling getrennt
async function processItem(id: string) {
  let item;
  try {
    item = getItemSync(id);  // könnte synchron werfen
  } catch (err) {
    handleError(err);
    return;
  }

  try {
    await saveItem(item);  // async
  } catch (err) {
    handleError(err);
  }
}

// Neu: Promise.try vereinheitlicht beides
async function processItem(id: string) {
  await Promise.try(() => getItemSync(id))
    .then(item => saveItem(item))
    .catch(handleError);
}

Promise.try(() => fn()) gibt immer ein Promise zurück – egal ob fn synchron wirft oder ein abgelehntes Promise zurückgibt. Ein Error-Handler für beide Fälle.

Map.prototype.getOrInsert

// Alt: häufiges Muster mit Boilerplate
const cache = new Map<string, User[]>();

function addToGroup(groupId: string, user: User) {
  if (!cache.has(groupId)) {
    cache.set(groupId, []);
  }
  cache.get(groupId)!.push(user);  // Non-null assertion nötig
}

// Neu: getOrInsert
function addToGroup(groupId: string, user: User) {
  cache.getOrInsert(groupId, []).push(user);
  //                              ^^^^ Rückgabetyp ist User[], kein undefined
}

Typsicher, ohne Non-null-Assertion. Besonders wertvoll in Grouping-Logik und Caches.

RegExp.escape

// Alt: eigene Escape-Funktion oder Library
function escapeRegex(str: string): string {
  return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}

const userInput = 'price: $100 (plus tax)';
const pattern = new RegExp(escapeRegex(userInput));

// Neu: nativ
const userInput = 'price: $100 (plus tax)';
const pattern = new RegExp(RegExp.escape(userInput));

Performance und DX-Verbesserungen

Subpath Imports

Interne Modul-Importe ohne relative Pfad-Hölle:

// package.json
{
  "imports": {
    "#utils/*": "./src/utils/*.ts",
    "#types/*": "./src/types/*.ts",
    "#config": "./src/config/index.ts"
  }
}
// Alt: relative Pfade
import { formatDate } from '../../../utils/date';
import type { User } from '../../types/user';

// Neu: Subpath Imports
import { formatDate } from '#utils/date';
import type { User } from '#types/user';

Der Vorteil: Dateien können verschoben werden, ohne alle Import-Pfade anpassen zu müssen. Und Tooling (IDE, Bundler) versteht die Aliases nativ – ohne zusätzliche Konfiguration in Vite, Webpack oder tsconfig paths.

TypeScript 6.0 unterstützt Subpath Imports vollständig mit korrektem Typ-Lookup.

Verbesserte Type Inference

// TS 5.x: `result` hat Typ `string | number | boolean`
function process<T extends string | number | boolean>(value: T) {
  const result = value;  // T, aber manchmal als union aufgelöst
  return result;
}

// TS 6.0: `result` behält Typ `T` konsistenter
// Weniger Stellen, an denen Generics unerwartet zu `any` oder breiten Unions expandieren

Konkret spürbar bei:

  • Chained generics in Higher-Order-Functions
  • Conditional Types in komplexen Utility-Typen
  • Inference durch Mapped Types

Bessere Fehlerdiagnose

TypeScript 6.0 verbessert die Ausgabe bei komplizierten Typfehlern. Statt verschachtelter „Type X is not assignable to type Y, which is not assignable to type Z”-Kaskaden gibt es präzisere Hinweise auf die Ursache.

Migration: Was konkret zu tun ist

Schritt 1: Upgrade und Fehler sichten

npm install typescript@6 --save-dev
npx tsc --noEmit 2>&1 | head -50

Die ersten Fehler werden wahrscheinlich von strict: true kommen – noImplicitAny, strictNullChecks. Das sind echte Bugs, keine falschen Positiven.

Schritt 2: --stableTypeOrdering aktivieren

{
  "compilerOptions": {
    "stableTypeOrdering": true
  }
}

Fehler hier sind Reihenfolge-Abhängigkeiten, die in TS 7.0 brechen würden. Meistens lösbar durch Umstrukturierung von Circular Dependencies.

Schritt 3: Schrittweise strict-Migration für bestehende Projekte

Wer nicht sofort alle strict-Fehler beheben will, kann einzelne Checks temporär deaktivieren:

{
  "compilerOptions": {
    "strict": true,
    "strictNullChecks": false  // temporär, bis Codebase bereit ist
  }
}

Die Reihenfolge macht Sinn: zuerst noImplicitAny lösen (die meisten Fehler), dann strictNullChecks (die tiefgreifendsten Änderungen).

Was sich nicht ändert

Bestehende tsconfig.json-Dateien mit expliziten Werten funktionieren weiterhin. TypeScript 6.0 ändert die Defaults, aber überschreibt keine gesetzten Werte. Eine Codebase mit "strict": false bleibt so, bis jemand das explizit ändert.

Einordnung

TypeScript 6.0 macht drei Dinge:

Schulden einfordern. strict: true als Default bedeutet, dass Code, der bisher zufällig funktionierte, jetzt Fehler zeigt. Das ist unangenehm im Upgrade – aber besser als diese Fehler in Production zu finden.

TS 7.0 vorbereiten. --stableTypeOrdering ist der einzige Weg, jetzt sicher zu sein, dass eine Codebase den Compiler-Wechsel zu Go übersteht.

Modernität erzwingen. module: esnext, target: ES2025 als Default setzt voraus, dass Teams mit modernen Toolchains arbeiten. Wer noch auf CommonJS oder alte Browser-Targets angewiesen ist, muss expliziter konfigurieren – aber das ist eine sinnvolle Umkehrung der Beweislast.

Der eigentliche Wert liegt nicht in den neuen APIs. Er liegt darin, dass TypeScript 6.0 eine Codebase in einen Zustand bringt, in dem der massive Performance-Sprung von TypeScript 7.0 ohne weiteren Aufwand wirksam wird.

Nicht jedes wichtige Release muss laut sein. TypeScript 6.0 ist genau deshalb relevant, weil es aufräumt, vorbereitet und modernisiert – nicht weil es mit Spektakel auftritt.