14 Jahre TypeScript: Wie ein Experiment von Microsoft zur Sprache des Webs wurde

14 Jahre TypeScript: Wie ein Experiment von Microsoft zur Sprache des Webs wurde

Vor 14 Jahren, am 1. Oktober 2012, stellte Anders Hejlsberg eine kleine Sprache vor, die JavaScript um Typen erweitern sollte. Die Reaktionen waren gemischt. Viele sahen darin einen weiteren Versuch von Microsoft, das offene Web in die eigene Richtung zu biegen, andere hielten Typen in einer dynamischen Skriptsprache schlicht für überflüssig. Heute ist TypeScript die meistgenutzte Sprache auf GitHub, fast jedes neue Frontend Projekt startet damit, und seit diesem Sommer läuft der Compiler nicht mehr in JavaScript, sondern nativ in Go.

In diesem Beitrag nehme ich dich mit auf eine lange Reise. Wir schauen uns an, warum JavaScript überhaupt so geworden ist, wie es ist, wieso TypeScript genau im richtigen Moment kam, was das Typsystem heute alles kann und warum es trotzdem berechtigte Kritik gibt. Zum Schluss geht es um TypeScript 7, das den Compiler um den Faktor zehn beschleunigt, und um die Frage, ob Typen eines Tages direkt in JavaScript landen. Es wird technisch, und es gibt viel Code.

Zehn Tage im Mai

Um TypeScript zu verstehen, muss man bei JavaScript anfangen. Im Mai 1995 bekam Brendan Eich bei Netscape den Auftrag, eine Skriptsprache für den Browser zu bauen. Sie sollte einfach sein, für Designer und Hobbyprogrammierer taugen und optisch an Java erinnern, weil Netscape damals eng mit Sun zusammenarbeitete. Eich schrieb den ersten Prototyp in rund zehn Tagen. Die Sprache hiess zuerst Mocha, dann LiveScript und im Dezember 1995 schliesslich JavaScript, ein Name, der vor allem Marketing war. Mit Java hatte sie ausser ein paar Schlüsselwörtern und geschweiften Klammern wenig gemeinsam.

Unter der Haube steckten ganz andere Vorbilder: Funktionen als Werte aus Scheme und prototypische Vererbung aus Self. Dazu kam eine sehr grosszügige automatische Typumwandlung, die Anfängern das Leben leichter machen sollte. Genau diese Grosszügigkeit wurde später zum Running Gag der ganzen Branche. Wer schon einmal ein paar Minuten mit der Browserkonsole gespielt hat, kennt solche Ergebnisse:

"5" - 2        // 3
"5" + 2        // "52"
[] + []        // ""
[] + {}        // "[object Object]"
typeof null    // "object"
null == 0      // false
null >= 0      // true
NaN === NaN    // false

Das sind keine Fehler im eigentlichen Sinn, sondern konsequent umgesetzte Regeln aus einer Zeit, in der niemand daran dachte, dass in dieser Sprache einmal ganze Büroanwendungen geschrieben würden. JavaScript sollte Formulare prüfen und Bilder austauschen, wenn die Maus darüber fährt. Mehr nicht.

1997 wurde die Sprache bei Ecma (private, internationale Normungsorganisation für Informations- und Kommunikationssysteme mit Sitz in Genf) als ECMA-262 standardisiert, unter dem Namen ECMAScript. Damit begann auch die lange und zähe Geschichte ihrer Weiterentwicklung.

Das verlorene Jahrzehnt

Was viele heute nicht mehr wissen: JavaScript hätte schon vor fast zwanzig Jahren Klassen, Module und optionale Typen bekommen können. Die geplante vierte Version, ECMAScript 4, sah genau das vor. Sie war stark von ActionScript 3 inspiriert, das Adobe in Flash einsetzte, und hätte die Sprache grundlegend umgebaut. Doch im Komitee gab es erbitterten Widerstand, unter anderem von Microsoft und Yahoo, die den Umbau für zu radikal hielten. Im August 2008 wurde ECMAScript 4 offiziell begraben. Stattdessen kam Ende 2009 das deutlich bescheidenere ECMAScript 5, und die grossen Ideen wanderten in ein Sammelprojekt namens Harmony, aus dem erst 2015 ECMAScript 2015 wurde.

Die Ironie dabei: Genau in diesen Jahren explodierte der Einsatz von JavaScript. Gmail erschien 2004, Google Maps 2005, und mit dem Begriff Ajax wurde klar, dass der Browser eine ernsthafte Anwendungsplattform ist. 2009 brachte Ryan Dahl mit Node.js JavaScript auf den Server. Plötzlich hatten Firmen Codebasen mit Hunderttausenden Zeilen JavaScript, aber eine Sprache, die für solche Grössenordnungen nie gedacht war. Kein Modulsystem, keine Klassen im heutigen Sinn, keine Typen und damit auch keine vernünftige Unterstützung im Editor. Wer eine Funktion umbenennen wollte, griff zu Suchen und Ersetzen und hoffte das Beste.

Die grossen Firmen bauten sich deshalb eigene Lösungen. Google entwickelte den Closure Compiler, der Typen aus speziell formatierten Kommentaren las:

/**
 * @param {string} name
 * @param {number} age
 * @return {string}
 */
function greet(name, age) {
  return name + " ist " + age + " Jahre alt";
}

Google hatte ausserdem GWT, mit dem man Java nach JavaScript übersetzen konnte, und stellte 2011 mit Dart gleich eine komplett neue Sprache vor, die JavaScript langfristig ersetzen sollte. In der Ruby und Python Szene war ab 2009 CoffeeScript beliebt, eine Sprache mit knapperer Syntax, die ebenfalls nach JavaScript kompiliert wurde. Auch bei Microsoft kämpften Teams wie jenes hinter Outlook Web Access mit riesigen Mengen JavaScript und experimentierten mit Werkzeugen wie Script#, das C# nach JavaScript übersetzte.

Alle diese Ansätze hatten ein gemeinsames Problem. Sie verlangten entweder eine neue Sprache mit eigener Semantik oder machten den Code umständlich. Was fehlte, war etwas, das JavaScript nicht ersetzt, sondern ergänzt.

Der 1. Oktober 2012

An dieser Stelle kommt Anders Hejlsberg ins Spiel. Der Däne hatte in den Achtzigern Turbo Pascal geschrieben, war bei Borland Chefarchitekt von Delphi und ab 2000 bei Microsoft der leitende Architekt von C#. Wenn es jemanden gab, der wusste, wie man Sprachen entwirft, die Entwickler gerne benutzen, dann ihn. Zusammen mit einem kleinen Team arbeitete er ab 2010 an einem Projekt, das intern zuerst unter dem Namen Strada lief.

Am 1. Oktober 2012 veröffentlichte Microsoft TypeScript 0.8. Die Grundidee war erstaunlich simpel und ist bis heute das Fundament der Sprache: TypeScript ist eine Obermenge von JavaScript. Jedes gültige JavaScript Programm ist auch ein gültiges TypeScript Programm. Du kannst eine bestehende Datei von .js in .ts umbenennen und schrittweise Typen hinzufügen. Der Compiler prüft die Typen und entfernt sie danach einfach wieder. Heraus kommt ganz normales JavaScript, das in jedem Browser läuft.

// Vorher: normales JavaScript, auch gültiges TypeScript
function area(width, height) {
  return width * height;
}

// Nachher: mit Typannotationen
function area(width: number, height: number): number {
  return width * height;
}

area(4, "5");
// Fehler: Argument of type 'string' is not assignable
// to parameter of type 'number'.

Und das erzeugte JavaScript sieht praktisch identisch aus wie das Original:

function area(width, height) {
  return width * height;
}

Zwei weitere Entscheidungen erwiesen sich als klug. Erstens wurde TypeScript von Anfang an unter der Apache Lizenz 2.0 als Open Source veröffentlicht, samt Online Playground, in dem man die Sprache direkt im Browser ausprobieren konnte. Zweitens orientierte sich das Team eng an den kommenden ECMAScript Standards. Klassen und Module, die in TypeScript 2012 verfügbar waren, folgten weitgehend dem, was später in ECMAScript 2015 standardisiert wurde. TypeScript wollte kein eigenes Universum bauen, sondern die Zukunft von JavaScript heute schon benutzbar machen.

Trotzdem war die Skepsis gross. Microsoft hatte im Web der Nullerjahre mit dem Internet Explorer 6 viel Vertrauen verspielt, und das alte Muster "Embrace, Extend, Extinguish" war vielen noch gut in Erinnerung. Warum sollte man ausgerechnet einer Sprache von Microsoft die Zukunft des eigenen Frontends anvertrauen?

Die Durchbruchsjahre

Die Antwort kam nicht über Nacht. TypeScript 1.0 erschien im April 2014 an der Konferenz Build. Im Sommer 2014 zog das Projekt von Microsofts eigener Plattform CodePlex auf GitHub um, was damals ein deutliches Signal war: Hier wird offen entwickelt, Issues und Pull Requests inklusive.

Gleichzeitig wuchs mit DefinitelyTyped eine riesige, von der Community gepflegte Sammlung von Typdefinitionen für bestehende JavaScript Bibliotheken. Damit konnte man jQuery, Lodash oder Express mit voller Unterstützung im Editor verwenden, obwohl diese Bibliotheken selbst gar nicht in TypeScript geschrieben waren. Heute installierst du solche Definitionen einfach über Pakete wie @types/node.

Der eigentliche Wendepunkt war aber Angular. Google arbeitete für Angular 2 an einer eigenen Erweiterung von JavaScript namens AtScript. Im März 2015 gaben Google und Microsoft bekannt, dass AtScript in TypeScript aufgeht und Angular 2 in TypeScript geschrieben wird. Dass ausgerechnet Google, der Konzern hinter Dart und dem Closure Compiler, auf eine Sprache von Microsoft setzte, war ein enormer Vertrauensbeweis.

Im selben Jahr erschien Visual Studio Code, ein Editor, der selbst in TypeScript geschrieben ist und dessen JavaScript Unterstützung vom TypeScript Language Service angetrieben wird. Das ist ein Punkt, der gerne übersehen wird: Auch wer nie eine einzige .ts Datei geschrieben hat, profitiert in VS Code bei der Autovervollständigung von JavaScript seit Jahren von TypeScript im Hintergrund.

Mit TypeScript 2.0 kam im September 2016 eine Funktion, die für mich bis heute die wichtigste überhaupt ist: strictNullChecks. Ohne diese Option ist null in jedem Typ erlaubt, was Tony Hoare einst als seinen "Milliardenfehler" bezeichnet hat. Mit der Option zwingt dich der Compiler, diesen Fall zu behandeln:

function findUser(id: number): User | undefined {
  return users.find((u) => u.id === id);
}

const user = findUser(42);
console.log(user.name);
// Fehler: 'user' is possibly 'undefined'.

if (user) {
  console.log(user.name); // hier ist user sicher ein User
}

console.log(user?.name ?? "Unbekannt"); // oder kurz so

Facebook versuchte ab 2014 mit Flow einen ähnlichen Ansatz, und eine Weile sah es nach einem echten Wettbewerb aus. Doch TypeScript hatte das bessere Tooling, die grössere Community und mit DefinitelyTyped die entscheidende Bibliothek an Typen. Heute spielt Flow ausserhalb von Meta kaum noch eine Rolle.

Was das Typsystem heute kann

TypeScript hat sich in den letzten Jahren von einem Werkzeug zum Annotieren von Parametern zu einem der ausdrucksstärksten Typsysteme entwickelt, die es in einer verbreiteten Sprache gibt. Ein paar Beispiele zeigen, was ich damit meine.

Strukturelle Typisierung

Anders als in C# oder Java zählt in TypeScript nicht der Name eines Typs, sondern seine Form. Wenn ein Objekt alle geforderten Eigenschaften hat, passt es. Das entspricht genau der Art, wie JavaScript ohnehin funktioniert:

interface Point {
  x: number;
  y: number;
}

function printPoint(p: Point) {
  console.log(`${p.x}, ${p.y}`);
}

const pixel = { x: 10, y: 20, color: "red" };
printPoint(pixel); // funktioniert, pixel hat x und y

Diskriminierte Unions

Eine meiner Lieblingsfunktionen. Du beschreibst mehrere Varianten eines Objekts mit einem gemeinsamen Feld, und der Compiler weiss in jedem Zweig genau, welche Variante vorliegt. Mit dem Typ never lässt sich sogar prüfen, dass kein Fall vergessen wurde:

type Shape =
  | { kind: "circle"; radius: number }
  | { kind: "rect"; width: number; height: number }
  | { kind: "triangle"; base: number; height: number };

function area(shape: Shape): number {
  switch (shape.kind) {
    case "circle":
      return Math.PI * shape.radius ** 2;
    case "rect":
      return shape.width * shape.height;
    case "triangle":
      return (shape.base * shape.height) / 2;
    default: {
      const unreachable: never = shape;
      throw new Error(`Unbekannte Form: ${unreachable}`);
    }
  }
}

Kommt später eine vierte Form dazu, meldet der Compiler sofort jede Stelle, an der sie noch nicht behandelt wird. Das ist Refactoring mit Netz und doppeltem Boden.

Generics und keyof

Generics erlauben Funktionen, die mit beliebigen Typen arbeiten, ohne die Typinformation zu verlieren. Zusammen mit keyof entstehen sehr präzise Signaturen:

function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
  return obj[key];
}

const post = { title: "14 Jahre TypeScript", views: 1200, draft: false };

const title = getProperty(post, "title"); // string
const views = getProperty(post, "views"); // number
getProperty(post, "author");
// Fehler: Argument of type '"author"' is not assignable
// to parameter of type '"title" | "views" | "draft"'.

Mapped und Conditional Types

Hier wird es richtig spannend. Mit Mapped Types kannst du aus bestehenden Typen neue ableiten, mit Conditional Types Logik auf Typebene schreiben. Viele eingebaute Hilfstypen wie Partial, Readonly oder ReturnType sind genau so gebaut:

// So ist Partial<T> im Kern definiert
type MyPartial<T> = {
  [K in keyof T]?: T[K];
};

// Nur die Schlüssel, deren Wert eine Funktion ist
type MethodKeys<T> = {
  [K in keyof T]: T[K] extends (...args: any[]) => any ? K : never;
}[keyof T];

class Api {
  baseUrl = "https://rueegger.me";
  getPosts() { return []; }
  getTags() { return []; }
}

type ApiMethods = MethodKeys<Api>; // "getPosts" | "getTags"

Template Literal Types

Seit TypeScript 4.1 lassen sich sogar Zeichenketten auf Typebene zusammensetzen. Damit kannst du zum Beispiel Eventnamen oder CSS Werte präzise beschreiben:

type Entity = "post" | "page" | "member";
type Action = "created" | "updated" | "deleted";

type EventName = `${Entity}.${Action}`;
// "post.created" | "post.updated" | ... | "member.deleted"

function on(event: EventName, handler: () => void) { /* ... */ }

on("post.created", () => {});   // ok
on("post.published", () => {}); // Fehler

satisfies

Der Operator satisfies aus TypeScript 4.9 löst ein altes Dilemma. Du willst prüfen, dass ein Objekt einem Typ entspricht, aber trotzdem den genaueren, abgeleiteten Typ behalten:

type Theme = Record<string, string | [number, number, number]>;

const colors = {
  primary: "#15171a",
  accent: [255, 92, 0],
} satisfies Theme;

colors.primary.toUpperCase(); // ok, TypeScript weiss: string
colors.accent.map((c) => c / 255); // ok, TypeScript weiss: Tupel

Hätte man hier const colors: Theme = ... geschrieben, wäre bei beiden Zugriffen nur noch string | [number, number, number] bekannt gewesen.

Diese Beispiele kratzen nur an der Oberfläche. Das Typsystem von TypeScript ist inzwischen sogar Turing vollständig, was Enthusiasten dazu gebracht hat, auf reiner Typebene Parser, Rechner und einmal sogar Doom zu bauen. Das ist faszinierend, führt uns aber direkt zu einem der Kritikpunkte, auf den ich weiter unten eingehe.

Warum TypeScript das moderne Web prägt

Im August 2025 passierte etwas, das man 2012 für einen Witz gehalten hätte. Laut dem Octoverse Bericht von GitHub überholte TypeScript Python und wurde, gemessen an der Zahl monatlicher Contributors, zur meistgenutzten Sprache auf der Plattform. Rund 2,64 Millionen Contributors schrieben TypeScript, etwa 42 000 mehr als bei Python, und JavaScript folgte mit 2,15 Millionen auf Platz drei. GitHub selbst nannte das die bedeutendste Verschiebung bei den Sprachen seit mehr als einem Jahrzehnt.

Die Gründe dafür sind vielfältig. Der offensichtlichste: Die grossen Frameworks setzen TypeScript heute als Standard. Next.js, Astro, SvelteKit, Angular, Nuxt und Vite legen neue Projekte direkt mit TypeScript an. Wer heute ein Frontend Projekt startet, muss sich aktiv gegen TypeScript entscheiden.

Der zweite Grund ist neuer und hat mit KI zu tun. GitHub verweist in seinem Bericht auf eine Studie, nach der 94 Prozent der Kompilierfehler in von Sprachmodellen erzeugtem Code Typfehler sind. Ein Typsystem ist damit so etwas wie ein automatischer Prüfer, der viele Fehler von KI Assistenten abfängt, bevor sie in Produktion landen. Zudem geben Typen den Modellen selbst mehr Kontext darüber, wie eine Funktion benutzt werden will. In einer Welt, in der immer mehr Code maschinell mitgeschrieben wird, ist das ein echter Vorteil.

Der dritte Grund ist vielleicht der wichtigste: TypeScript ist inzwischen direkt in den Laufzeitumgebungen angekommen. Deno konnte TypeScript von Beginn an ausführen, Bun ebenso. Und Node.js hat nachgezogen. Seit Version 23.6 und 22.18 ist das sogenannte Type Stripping standardmässig aktiv, seit 25.2 und 24.12 gilt es offiziell als stabil. Du kannst heute also einfach das hier tun:

// server.ts
import { createServer } from "node:http";

interface Health {
  status: "ok" | "degraded";
  uptime: number;
}

createServer((req, res) => {
  const body: Health = { status: "ok", uptime: process.uptime() };
  res.writeHead(200, { "Content-Type": "application/json" });
  res.end(JSON.stringify(body));
}).listen(3000);
node server.ts

Kein Buildschritt, keine Konfiguration. Node entfernt die Typen beim Laden und führt den Rest aus. Wichtig ist dabei: Node prüft die Typen nicht, es ignoriert sie nur. Für die eigentliche Prüfung brauchst du weiterhin tsc, typischerweise im Editor und in der CI.

Das hat eine interessante Nebenwirkung auf die Sprache selbst. Node kann nur Syntax entfernen, die keinerlei Laufzeitverhalten hat. Einige ältere Funktionen von TypeScript erzeugen aber echten JavaScript Code, allen voran enum, Namespaces mit Werten und Parameter Properties in Klassen. Diese laufen in Node ohne Transformation nicht. TypeScript 5.8 hat deshalb die Option erasableSyntaxOnly eingeführt, die genau diese Konstrukte verbietet. In der Praxis ersetzt man ein Enum heute oft durch ein Objekt mit as const:

// Alt: erzeugt Laufzeitcode, läuft nicht mit Type Stripping
enum Status {
  Draft = "draft",
  Published = "published",
}

// Neu: reines JavaScript plus Typen
const Status = {
  Draft: "draft",
  Published: "published",
} as const;

type Status = (typeof Status)[keyof typeof Status];
// "draft" | "published"

Damit wird TypeScript in gewisser Weise wieder das, was es 2012 sein wollte: JavaScript mit Typen, nicht mehr und nicht weniger.

Die Kritik: Nicht alles ist Gold

Ich schreibe TypeScript gerne und möchte in grösseren Projekten nicht mehr darauf verzichten. Trotzdem wäre ein Jubiläumsartikel ohne kritischen Blick unehrlich. Es gibt gute Gründe, warum manche Entwickler TypeScript bewusst meiden.

Typen sind zur Laufzeit weg

Das ist gleichzeitig die grösste Stärke und die grösste Schwäche. Weil Typen einfach entfernt werden, gibt es zur Laufzeit keinerlei Garantie. Alles, was von aussen kommt, also API Antworten, Formulardaten oder JSON aus einer Datei, ist in Wahrheit ungeprüft. TypeScript glaubt dir, was du ihm sagst:

interface Member {
  email: string;
  tier: "basis" | "werbefrei" | "komplett";
}

const data = JSON.parse(await res.text()) as Member;
data.email.toLowerCase();
// Kompiliert problemlos, stürzt aber ab,
// wenn die API { "mail": "..." } liefert.

Wer robusten Code will, muss Daten an den Systemgrenzen zusätzlich validieren, entweder von Hand oder mit Bibliotheken wie Zod oder Valibot. Von Hand sieht das etwa so aus:

function isMember(value: unknown): value is Member {
  return (
    typeof value === "object" &&
    value !== null &&
    typeof (value as Member).email === "string" &&
    ["basis", "werbefrei", "komplett"].includes((value as Member).tier)
  );
}

const data: unknown = JSON.parse(await res.text());
if (!isMember(data)) throw new Error("Ungültige Antwort");
data.email.toLowerCase(); // jetzt wirklich sicher

Man beschreibt also dieselbe Struktur zweimal, einmal für den Compiler und einmal für die Laufzeit. Das ist lästig und ein Grund, warum Bibliotheken, die den Typ direkt aus dem Schema ableiten, so beliebt geworden sind.

Das Typsystem ist bewusst nicht korrekt

TypeScript ist ein pragmatisches Typsystem, kein mathematisch korrektes. Das Team hat sich absichtlich für einige Lücken entschieden, damit sich typisches JavaScript überhaupt vernünftig beschreiben lässt. Ein klassisches Beispiel ist die Kovarianz von Arrays:

class Animal { name = "Tier"; }
class Dog extends Animal { bark() { console.log("Wuff"); } }
class Cat extends Animal { meow() { console.log("Miau"); } }

const dogs: Dog[] = [new Dog()];
const animals: Animal[] = dogs; // erlaubt
animals.push(new Cat());        // ebenfalls erlaubt

dogs[1].bark();
// Kompiliert, wirft aber zur Laufzeit:
// TypeError: dogs[1].bark is not a function

Dazu kommen any, das jede Prüfung abschaltet und sich still durch eine Codebasis ausbreiten kann, sowie Typzusicherungen mit as, die im Grunde sagen: «Vertrau mir, ich weiss es besser.» Ein grüner Durchlauf von tsc bedeutet also nicht, dass dein Programm keine Typfehler hat. Er bedeutet nur, dass der Compiler keine gefunden hat.

Typgymnastik

Ich habe oben gezeigt, wie mächtig Mapped und Conditional Types sind. Die Kehrseite: In manchen Bibliotheken sind Typdefinitionen entstanden, die kaum noch jemand versteht. Fehlermeldungen über zwanzig Zeilen, in denen sich verschachtelte Generics gegenseitig zitieren, sind keine Seltenheit. Ein harmloses Beispiel dafür, wie schnell das kippt:

type DeepReadonly<T> = T extends (infer U)[]
  ? ReadonlyArray<DeepReadonly<U>>
  : T extends Function
    ? T
    : T extends object
      ? { readonly [K in keyof T]: DeepReadonly<T[K]> }
      : T;

Das ist noch lesbar. Aber es zeigt, wie aus einer Sprache für bessere Wartbarkeit eine zweite Programmiersprache auf Typebene wird, die eigene Expertise verlangt. Für kleine Teams und einfache Projekte kann das mehr kosten, als es bringt.

Tooling und Konfiguration

Wer schon einmal versucht hat, eine gemischte Codebasis mit CommonJS und ES Modulen, Jest, einem Bundler und tsconfig.json sauber zum Laufen zu bringen, weiss, wovon ich spreche. Optionen wie moduleResolution, esModuleInterop oder paths haben über Jahre viel Frust erzeugt. Das hat sich mit modernen Standardwerten und Type Stripping deutlich verbessert, aber das Erbe ist in vielen bestehenden Projekten noch spürbar.

Prominente Abkehr

2023 sorgten zwei Ankündigungen für Aufsehen. David Heinemeier Hansson, der Erfinder von Ruby on Rails, entfernte TypeScript aus der Bibliothek Turbo 8 und begründete das damit, dass die Typen für ihn mehr Aufwand als Nutzen brachten. Etwa zur gleichen Zeit stellte Rich Harris den Quellcode von Svelte selbst von TypeScript auf JavaScript mit JSDoc Annotationen um. Wichtig dabei: Svelte hat TypeScript nicht aufgegeben, die Typprüfung läuft weiterhin über den TypeScript Compiler, nur ohne eigenen Buildschritt für die Bibliothek. Beide Fälle zeigen aber, dass TypeScript nicht für jedes Projekt die offensichtliche Wahl ist.

Abhängigkeit von einem Konzern

Und dann ist da noch die Frage der Steuerung. TypeScript ist Open Source, aber die Sprache wird faktisch von Microsoft entwickelt und gesteuert. Es gibt keine unabhängige Stiftung und kein Standardkomitee. Bisher hat Microsoft dieses Vertrauen nicht missbraucht, im Gegenteil. Aber wenn die meistgenutzte Sprache auf GitHub in der Hand eines einzelnen Unternehmens liegt, darf man das als Open Source Mensch durchaus kritisch sehen.

TypeScript 6 und 7: Ein Compiler zieht um

Die grösste technische Veränderung in der Geschichte von TypeScript hat 2025 begonnen. Im März 2025 kündigte Anders Hejlsberg an, dass der Compiler komplett nach Go portiert wird. Das Projekt lief unter dem Codenamen Corsa, während die bisherige Codebasis in JavaScript nun offiziell Strada hiess, eine Anspielung auf den ursprünglichen internen Namen von 2010.

Der Grund war simpel: Performance. Der alte Compiler war in TypeScript geschrieben und lief damit in einer JavaScript Engine, also mit nur einem Thread und dem Overhead einer dynamischen Laufzeit. Bei sehr grossen Projekten wie VS Code dauerte ein vollständiger Durchlauf mehrere Minuten, und auch der Editor wurde bei grossen Codebasen spürbar zäh.

Die Wahl der Sprache sorgte für Diskussionen. Warum nicht Rust, das in der Welt der Webwerkzeuge mit SWC, Biome oder Oxc so erfolgreich ist? Oder C#, Hejlsbergs eigene Sprache? Die Antwort des Teams war pragmatisch. Go ist strukturell dem bestehenden Code sehr ähnlich, hat eine automatische Speicherverwaltung und erlaubt trotzdem viel Kontrolle über das Speicherlayout. Damit liess sich der Compiler weitgehend Datei für Datei portieren, ohne die Architektur neu zu erfinden. Mit Rust hätte man wegen der vielen zyklischen Datenstrukturen im Compiler grosse Teile neu entwerfen müssen, und das Verhalten exakt gleich zu halten, wäre deutlich schwieriger geworden.

Den Übergang hat das Team in zwei Schritten gelöst. TypeScript 6.0 erschien am 23. März 2026 als letzte Version auf Basis des alten JavaScript Compilers. Sie war vor allem eine Brücke und hat viele Standardwerte modernisiert und alte Zöpfe abgeschnitten. So ist strict jetzt standardmässig aktiv, module steht auf esnext, target auf es2025, und types bindet nicht mehr automatisch alle installierten @types Pakete ein. Gleichzeitig wurden target: es5, baseUrl für die Modulauflösung und die alte Auflösung node als veraltet markiert, die Modulformate AMD, UMD und SystemJS sowie outFile sind ganz verschwunden.

Am 8. Juli 2026 folgte dann TypeScript 7.0 mit dem nativen Compiler. Die Zahlen, die Microsoft veröffentlicht hat, sind beeindruckend:

Projekt       TypeScript 6    TypeScript 7    Faktor
VS Code       125,7 s         10,6 s          11,9x
Sentry        139,8 s         15,7 s           8,9x
Bluesky        24,3 s          2,8 s           8,7x
Playwright     12,8 s          1,47 s          8,7x

Dazu kommen je nach Projekt sechs bis 26 Prozent weniger Speicherverbrauch. Im Editor zeigt sich der Unterschied noch deutlicher: Die Zeit, bis VS Code nach dem Öffnen einer Datei die Fehler anzeigt, sank im Test von 17,5 Sekunden auf unter 1,3 Sekunden. Der neue Language Server basiert direkt auf dem Language Server Protocol, und laut Microsoft gibt es über 60 Prozent weniger Abstürze.

Ein grosser Teil des Gewinns kommt von echter Parallelisierung. Der neue Compiler prüft Typen standardmässig mit vier Workern, was sich einstellen lässt:

# Installation, das Paket heisst weiterhin typescript
npm install -D typescript

# Normaler Build, standardmässig mit 4 Type Checkern
npx tsc

# Mehr Worker auf einer starken Maschine
npx tsc --checkers 8

# Projektreferenzen parallel bauen
npx tsc --build --builders 4

# Zum Debuggen alles in einem Thread
npx tsc --singleThreaded

Mit acht Checkern erreicht der Build von VS Code sogar einen Faktor von 16,7. Für ein neues Projekt mit TypeScript 7 und Node reicht heute eine erfreulich kurze Konfiguration:

{
  "compilerOptions": {
    "module": "nodenext",
    "target": "es2025",
    "types": ["node"],
    "noEmit": true,
    "erasableSyntaxOnly": true,
    "verbatimModuleSyntax": true
  },
  "include": ["src"]
}

strict muss nicht mehr erwähnt werden, weil es inzwischen Standard ist. Mit noEmit und erasableSyntaxOnly nutzt du tsc nur noch als Prüfer, während Node die Dateien direkt ausführt.

Ganz ohne Haken ist der Umstieg aber nicht. TypeScript 7.0 hat noch keine stabile programmatische API, die soll erst mit Version 7.1 kommen. Das betrifft vor allem Werkzeuge, die den Compiler intern verwenden. Vorlagen in Vue, Svelte, Astro, Angular und MDX werden vorerst nicht unterstützt. Wer damit arbeitet, braucht weiterhin TypeScript 6, das sich über das Paket @typescript/typescript6 mit dem Befehl tsc6 parallel installieren lässt. Auch bei der Unterstützung von reinem JavaScript mit JSDoc wurde aufgeräumt, einige exotische Muster aus dem Closure Umfeld funktionieren nicht mehr.

Für mich ist TypeScript 7 trotzdem ein Meilenstein. Nicht wegen neuer Sprachfunktionen, denn davon gibt es kaum welche, sondern weil ein Werkzeug, das Millionen Entwickler jeden Tag benutzen, auf einen Schlag zehnmal schneller geworden ist. Das spürt man bei jedem Speichern.

Ausblick: Typen direkt in JavaScript?

Bleibt die grosse Frage, ob TypeScript eines Tages überflüssig wird, weil JavaScript selbst Typen bekommt. 2022 wurde bei TC39, dem Komitee hinter ECMAScript, ein Vorschlag mit dem Namen Type Annotations eingebracht, oft auch «Types as Comments» genannt. Die Idee: JavaScript Engines sollen Typannotationen einfach als Kommentare behandeln und ignorieren. Code wie dieser würde dann direkt im Browser laufen:

// Vision des Vorschlags: gültiges JavaScript, ohne Buildschritt
function add(a: number, b: number): number {
  return a + b;
}

Die Prüfung würde weiterhin ein externes Werkzeug wie TypeScript übernehmen, aber der Buildschritt fiele weg. Der Vorschlag steckt allerdings seit 2022 in Stufe 1, also ganz am Anfang des Prozesses. Die Fragen sind schwierig: Welche Syntax soll reserviert werden, wie geht man mit Generics um, deren spitze Klammern mit Vergleichsoperatoren kollidieren können, und wie verhindert man, dass die Sprache an eine bestimmte Typprüfung gebunden wird? Ich rechne ehrlich gesagt nicht damit, dass wir das in den nächsten Jahren im Browser sehen.

Vielleicht ist das auch gar nicht mehr so wichtig. Mit Type Stripping in Node, Deno und Bun, mit erasableSyntaxOnly und mit einem Compiler, der in Sekunden statt Minuten prüft, ist TypeScript praktisch schon dort angekommen, wo der Vorschlag hinwill. Nur eben ohne Standard.

Fazit

Als TypeScript vor 14 Jahren erschien, war es eine von vielen Antworten auf die Frage, wie man grosse Anwendungen in JavaScript schreiben kann. Dart wollte JavaScript ersetzen, CoffeeScript wollte es schöner machen, Flow wollte dasselbe wie TypeScript. Gewonnen hat die bescheidenste Idee: JavaScript nicht zu verändern, sondern nur zu beschreiben.

Diese Zurückhaltung ist bis heute der Kern des Erfolgs. TypeScript hat nie versucht, schlauer als die Plattform zu sein, sondern hat sich konsequent am Standard orientiert und alte Eigenheiten wie Enums nach und nach zurückgedrängt. Gleichzeitig hat es die Art, wie wir Frontends bauen, grundlegend verändert. Autovervollständigung, sicheres Refactoring und frühe Fehlermeldungen sind für die meisten von uns so selbstverständlich geworden, dass wir kaum noch merken, wie viel davon auf TypeScript zurückgeht.

Die Kritik bleibt berechtigt. Typen ohne Laufzeitgarantie, ein bewusst lückenhaftes Typsystem, gelegentliche Typgymnastik und die starke Rolle von Microsoft sind echte Punkte, die man kennen sollte. Aber mit TypeScript 7 hat das Team gezeigt, dass es auch nach 14 Jahren bereit ist, das eigene Fundament grundlegend umzubauen, wenn es den Nutzern hilft.

Alles Gute zum Geburtstag, TypeScript. Und ich bin gespannt, was die nächsten 14 Jahre bringen.

Wie siehst du das? Setzt du TypeScript in deinen Projekten ein, bist du schon auf Version 7 umgestiegen oder bleibst du bewusst bei JavaScript? Schreib es mir gerne in die Kommentare.