TypeScript ist kein Konkurrent zu JavaScript, sondern eine strikte Erweiterung davon. Jedes valide JS-Programm ist auch ein valides TS-Programm — TypeScript ergänzt lediglich eine statische Typebene, die zur Compile-Zeit geprüft und vor der Ausführung wieder entfernt wird. Diese Eigenschaft als Superset ist der Schlüssel zu allem, was im Alltag mit TS funktioniert: schrittweise Migration, gemischte Codebases, JSDoc-Typen in .js-Dateien. Dieser Artikel grenzt die beiden Sprachen sauber voneinander ab, zeigt die JSDoc-Variante als TS-light und beschreibt einen pragmatischen Migrationspfad. Am Ende weißt du, wann sich der Wechsel lohnt — und wann du bei JS bleiben solltest.
Das Superset-Versprechen
Microsoft bewirbt TypeScript seit dem ersten Release 2012 mit einer einzigen, präzisen Formulierung: „TypeScript is a typed superset of JavaScript that compiles to plain JavaScript." Das bedeutet konkret:
- Jede
.js-Datei lässt sich in.tsumbenennen und ist sofort valider TypeScript-Code. - Alle JS-Sprachfeatures funktionieren in TS identisch — Closures, Prototypen,
async/await, Destructuring, Optional Chaining. - Der TS-Compiler entfernt nur Typen, er ändert keine Semantik. Was du in TS schreibst, läuft nach dem Compile-Schritt wie JS.
Was TypeScript on top mitbringt:
- Statische Typ-Annotationen —
let name: string = "Anna"statt nurlet name = "Anna". - Interfaces und Type Aliases — strukturelle Typen für Objekte, Funktionen und Klassen.
- Generics — Type Parameters für wiederverwendbare, typsichere Abstraktionen.
- Enums — nominale Enumerations (mit Einschränkungen, siehe
const enum). satisfies— Operator zur Validierung ohne Verlust der konkreten Inferenz (seit TS 4.9).- Decorators — auf TC39-Stage-3 ab TS 5.0; die alten Experimental-Decorators bleiben optional verfügbar.
- Utility Types —
Partial,Pick,Omit,Record,ReturnTypeund viele mehr aus der Stdlib der Types.
Ein minimales Beispiel zeigt den Unterschied:
// TypeScript: explizite Typen
function greet(name: string): string {
return `Hallo, ${name}!`;
}
greet("Anna"); // ok
greet(42); // Fehler: Argument of type 'number'...In JS würde der zweite Aufruf zur Laufzeit "Hallo, 42!" erzeugen — TS verhindert ihn schon im Editor.
Was TypeScript nicht tut
So viel TS hinzufügt — manche Erwartungen erfüllt es bewusst nicht:
- Keine eigene Runtime. TS läuft nicht. Was läuft, ist das emittierte JavaScript (oder Type-gestripptes JS in Node 22+/Bun/Deno).
- Keine eigene Standardbibliothek. Es gibt keine TS-spezifischen
Array.prototype-Methoden. Alle Runtime-APIs kommen von JS-Engines, Browsern, Node oder Drittpaketen. - Keine eigenen Module. TS nutzt ES Modules und CommonJS — exakt wie JS. Was an Modul-Syntax existiert, ist Standard-JS.
- Keine Runtime-Validierung. Wenn ein API-Response zur Laufzeit nicht der Typ-Deklaration entspricht, merkt TS davon nichts. Dafür braucht es Libraries wie Zod, Valibot oder ArkType.
- Keine Performance-Optimierung. Der emittierte Code ist im Wesentlichen das, was du geschrieben hast, ohne Typen. Schneller wird er dadurch nicht.
Diese Abgrenzung ist wichtig: TypeScript ist ein Linter mit eingebautem Type-Checker — kein Framework, keine Plattform.
Vergleich auf einen Blick
| Aspekt | JavaScript | TypeScript |
|---|---|---|
| Type-Checking | dynamisch, zur Laufzeit | statisch, zur Compile-Zeit |
| Datei-Endungen | .js, .mjs, .cjs | .ts, .tsx, .mts, .cts |
| Build-Schritt | keiner zwingend | tsc, tsx, esbuild, swc (oder nativ in Bun/Deno) |
| IDE-Support | gut, mit Heuristik | exzellent, basierend auf Typ-Informationen |
| Refactoring | textbasiert, fehleranfällig | typgestützt, sicher über Projekt-Grenzen |
| Runtime-Overhead | keiner | keiner (Typen werden entfernt) |
| Lernkurve | flach für Einsteiger | mittel — Typ-System will gelernt werden |
| Ökosystem | NPM, Browser, Node | NPM + DefinitelyTyped (@types/*) |
| Fehlerfindung | beim Ausführen oder Testen | im Editor, vor dem Speichern |
| Code-Größe | unverändert | minimal kleiner nach Strip; geringfügig größer mit Decorators |
| Standards-Einfluss | TC39-Output | TC39-Input (z. B. Decorators, Pipeline-Operator) |
Die Tabelle zeigt: Der eigentliche Unterschied liegt nicht im Runtime-Verhalten — sondern in dem, was du beim Schreiben des Codes weißt.
JSDoc als TS-light
Es gibt einen oft übersehenen Mittelweg: TS-Type-Checking in .js-Dateien über JSDoc-Kommentare. Aktiviert wird das per // @ts-check-Pragma in der Datei oder per checkJs: true in der tsconfig.json. Die Engine darunter ist exakt derselbe TS-Compiler — gleiche Diagnostics, gleiche Inferenz.
// @ts-check
/**
* @typedef {Object} User
* @property {string} name
* @property {number} age
* @property {string} [email]
*/
/**
* @param {User} user
* @returns {string}
*/
function formatUser(user) {
return `${user.name} (${user.age})`;
}
formatUser({ name: "Anna", age: 30 }); // ok
formatUser({ name: "Bob" }); // Fehler: 'age' fehltVorteile der JSDoc-Variante:
- Kein Build-Step.
.jsläuft direkt — im Browser, in Node, überall. - Schrittweise einführbar. Du fügst
// @ts-checkDatei für Datei hinzu. - Funktioniert mit jedem JS-Tool. Linters, Bundler, Minifier ignorieren die Kommentare einfach.
- Gleicher Editor-Support. VS Code liefert für JSDoc-Typen praktisch dieselbe Erfahrung wie für
.ts.
Grenzen der JSDoc-Variante:
- Verbose. Mehrzeilige Kommentare statt knapper Inline-Annotations.
- Kein
satisfies-Operator. Wohl aber ab TS 5.6 ein@satisfies-Tag — mit Einschränkungen. - Mapped Types und Conditional Types sind nur über
@typedefmit komplexer Syntax möglich. - Decorators lassen sich nicht in JS-Dateien nutzen.
- Type-Imports brauchen die
@import-Tag-Syntax (seit TS 5.5).
Für Library-Code, der ohne Build-Step ausgeliefert werden soll, ist JSDoc oft die elegantere Lösung. Frameworks wie Preact und SvelteKit haben Teile ihrer Codebases lange so betrieben.
Gradual Typing mit allowJs und checkJs
Der pragmatische Mittelweg zwischen „alles JS" und „alles TS" heißt gradual typing. Zwei tsconfig-Schalter machen ihn möglich:
{
"compilerOptions": {
"target": "ES2022",
"module": "ESNext",
"moduleResolution": "Bundler",
"allowJs": true, // .js-Dateien dürfen importiert werden
"checkJs": true, // .js-Dateien werden ebenfalls type-checked
"strict": true,
"noEmit": true // wenn ein Bundler den Output erzeugt
},
"include": ["src/**/*"]
}allowJs— der Compiler akzeptiert.js-Dateien als Input. Imports zwischen.tsund.jsfunktionieren.checkJs— JS-Dateien werden mitgeprüft (wie ein globales// @ts-check). Einzelne Dateien lassen sich mit// @ts-nocheckausnehmen.@types/*aus DefinitelyTyped liefert Typen für JS-Libraries, die selbst keine mitbringen.
Damit kannst du in einer existierenden JS-Codebase ohne Rename-Welle anfangen: TS-Konfiguration anlegen, ein paar Module umstellen, Diagnostics schrittweise abräumen.
Wann TS, wann JS?
Die Wahl ist keine Glaubensfrage — sie hängt von Projektgröße, Lebensdauer und Team ab.
TypeScript ist die richtige Wahl, wenn:
- Der Code länger als ein paar Wochen lebt und mehrfach refactored wird.
- Mehrere Personen daran arbeiten — Typen sind eingebaute Dokumentation.
- Das Projekt ein größeres Datenmodell hat (Entities, API-Schnittstellen, Domain-Logik).
- Es öffentliche APIs veröffentlicht, deren Verträge stabil bleiben sollen.
- Ein Build-Step ohnehin existiert — bei Frameworks wie React, Vue, Svelte, Astro ist das Standard.
JavaScript bleibt sinnvoll bei:
- Kleinen Scripten (unter 200 Zeilen, einmaliger Zweck).
- Hot-Patches und Quick-Fixes ohne Toolchain-Zugriff.
- Lehr-Material und Tutorials, wo der Fokus auf Sprachkonzepten liegen soll.
- Snippets in Slides, REPL-Sessions, Blog-Posts.
- Browser-Inline-Skripten, die ohne Build laufen müssen — auch hier hilft optional JSDoc.
Mittelweg: JS mit JSDoc und // @ts-check für Projekte, die Typen wollen, aber keinen Build-Step.
Migration-Pfad: pragmatische Schritte
Wer eine existierende JS-Codebase migrieren will, fährt am besten in dieser Reihenfolge:
1. TypeScript installieren — lokal, nicht global.
npm install --save-dev typescript
npx tsc --init2. Minimale tsconfig.json mit allowJs.
{
"compilerOptions": {
"target": "ES2022",
"module": "ESNext",
"moduleResolution": "Bundler",
"allowJs": true,
"checkJs": false,
"noEmit": true,
"skipLibCheck": true,
"esModuleInterop": true
},
"include": ["src/**/*"]
}3. npx tsc --noEmit ausführen — siehst du wenige oder keine Fehler, läuft die Basis bereits.
4. @types/* für genutzte Libraries installieren. Für Lodash etwa npm install --save-dev @types/lodash. Tools wie are-the-types-wrong helfen, fehlende oder schlechte Typings zu identifizieren.
5. Datei für Datei umbenennen. Beginne mit Modulen, die wenige Imports haben — Utilities, Konstanten, kleine Helper. Wo eine Datei nicht migriert werden soll, hilft // @ts-check und JSDoc.
6. strict-Family schrittweise aktivieren. Reihenfolge nach Schmerz:
noImplicitAnyzuerst — zwingt zu expliziten Typen.strictNullChecksals nächstes — meist die meiste Arbeit, aber der größte Gewinn.noImplicitThis,strictFunctionTypes,alwaysStrictfolgen typischerweise problemlos.
7. CI absichern. tsc --noEmit als Schritt in der Pipeline, sodass neue Type-Fehler nicht durchrutschen.
Größere Projekte (Airbnb, Slack, Lyft) berichten von Migrations-Zeiträumen zwischen sechs und achtzehn Monaten — graduell, ohne Feature-Freeze.
Trends in der Praxis (Stand 2026)
Wo steht das TS/JS-Verhältnis heute?
- Frontend-Frameworks sind TS-first. React (offizielle Templates), Vue 3, Svelte 5, Solid, Astro — alle dokumentieren primär TS, JS-Snippets sind die Ausnahme.
- Node.js mit TS läuft praktisch durchgängig über
tsxoderts-nodeim Dev-Mode; produktiv kompiliert man meist mittsc,esbuildoderswc. - Bun und Deno führen TS nativ aus — kein Build-Step, kein Loader.
- Node 22+ hat
--experimental-strip-typeseingeführt: Native Type-Stripping, sodass.ts-Dateien direkt ausführbar sind. Ab Node 23+ standardmäßig aktiv. - State-of-JS-Daten der letzten Jahre zeigen TS-Anteile von über 80 Prozent unter Frontend-Entwicklern — und steigend.
- JSR (JavaScript Registry), das von Deno-Team ins Leben gerufene Package-Repository, behandelt TS als First-Class-Citizen: Pakete liefern Sources, keine kompilierten Artefakte.
TypeScript ist damit kein optionales Add-on mehr, sondern in vielen Bereichen die Default-Sprache der JS-Welt — auch wenn JS in Engines, Browsern und kleinen Skripten unverändert weiterlebt.
Interessantes
TypeScript ist TC39-Member.
Microsoft sitzt mit dem TS-Team im TC39-Komitee und beeinflusst aktiv die JavaScript-Standardisierung. Features wie Decorators (Stage 3) oder der Pipeline-Operator kommen teils direkt aus TS-Vorschlägen — TS dient als Inkubator für die Sprachebene.
JSDoc-Type-Checking nutzt die TS-Engine.
Wenn du in einer .js-Datei // @ts-check schreibst, läuft genau derselbe Compiler, der auch .ts-Dateien prüft. Diagnostics, Inferenz, Library-Types — alles identisch. Der einzige Unterschied ist die Annotation-Syntax.
Bun und Deno führen TypeScript nativ aus.
Beide Runtimes haben einen integrierten TS-Loader, der Typen on-the-fly strippt — kein tsc, kein tsx, kein Build-Verzeichnis. Für viele Skripte und kleine Server ist das eine Vereinfachung, die TS praktisch unsichtbar macht.
ts-ignore und ts-expect-error als gezielte Outs.
Mit // @ts-ignore unterdrückst du eine Fehlerzeile pauschal. // @ts-expect-error ist besser: Es erwartet einen Fehler — wenn keiner mehr auftritt (etwa nach einem Refactor), schlägt der Compiler Alarm. So bleibt technische Schuld sichtbar.
DefinitelyTyped als Schatten-Ökosystem.
Das @types/*-Namespace auf NPM ist ein Community-Projekt mit über 8.000 Paketen, die Typen für JS-Libraries nachliefern, die selbst keine bringen. Ohne DefinitelyTyped wäre die TS-Adoption deutlich langsamer verlaufen.
TS hat Vanilla-JS in Frontend-Frameworks 2024 überholt.
Laut State-of-JS-Erhebungen kreuzten die Adoption-Kurven 2023/2024 — bei Greenfield-Projekten in React, Vue und Svelte ist TS heute der Default. Vanilla-JS bleibt stark in Lehr-Material, Tutorials und kleinen Tools.
Type-Stripping in Node.js reift seit Version 22.
Der Flag --experimental-strip-types entfernt Type-Annotationen, ohne den Code zu transpilen. Damit lassen sich .ts-Dateien direkt ausführen — ohne tsx, ohne Build. Ein Schritt in Richtung „TS als reine Annotation-Sprache".
ts-blank-space als Minimal-Strip-Strategie.
Bloombergs ts-blank-space ersetzt Typen durch Whitespace statt sie zu entfernen — so bleiben Sourcemaps unnötig, und der Strip ist O(n). Der Vorschlag treibt aktuell die Idee, TS-zu-JS-Konvertierung als Library-freie Operation im Standard zu etablieren.