Suchen

Vergleich · Programmierung & Entwicklung

TypeScript vs. JavaScript: Unterschiede, Beispiele und Umstieg

TypeScript ist JavaScript mit einem statischen Typsystem, das vor der Ausführung Fehler meldet – zur Laufzeit ist am Ende aber trotzdem JavaScript. Hier siehst du den Unterschied an echtem Code und Compiler-Ausgaben.

JavaScript-Quelltext mit bunter Syntaxhervorhebung auf dunklem Bildschirm

Das Wichtigste in Kürze

  • TypeScript prüft Typen beim Kompilieren oder im Editor; die Typen werden entfernt und existieren zur Laufzeit nicht mehr.
  • Ein Typfehler stoppt die Ausführung nicht automatisch: Node.js kann TypeScript-Dateien ohne Typprüfung ausführen, nur tsc meldet die Fehler.
  • Daten von außen (JSON, API-Antworten) sind zur Laufzeit ungeprüft – dafür ist unknown sicherer als any.
  • Der Umstieg geht schrittweise: erst JSDoc und // @ts-check in JavaScript-Dateien, dann .ts-Dateien, zuletzt strict.

Der Kern: Typen, die verschwinden

TypeScript ist laut der offiziellen Dokumentation eine Obermenge von JavaScript: Es unterstützt alle JavaScript-Features und legt ein Typsystem darüber. Gültiges JavaScript ist damit auch gültiges TypeScript, und du kannst schrittweise umsteigen.

Dazu gehört ein Punkt, der in Vergleichen oft untergeht: Die Typen existieren nur beim Entwickeln. Sie werden vor der Ausführung entfernt (Type Erasure), übrig bleibt JavaScript. TypeScript fügt also keine Prüfungen zur Laufzeit hinzu. Es verhindert Fehler, indem es deinen Code analysiert, bevor er läuft, im Editor oder mit dem Compiler tsc.

Derselbe Code in JavaScript und TypeScript

Eine kleine Funktion, die den Bruttopreis aus Netto und Steuersatz berechnet. Zuerst JavaScript:

JavaScript
function bruttopreis(netto, steuersatz) {
  return netto * (1 + steuersatz);
}

console.log(bruttopreis(100, 0.19));
console.log(bruttopreis("100", 0.19)); // läuft ohne Fehler
console.log(bruttopreis(100, "19%"));  // läuft ohne Fehler
Text
119
119
NaN

JavaScript wandelt den String "100" stillschweigend in eine Zahl um, und aus "19%" wird NaN. Beides sind typische Fehlerquellen, die erst auffallen, wenn jemand das Ergebnis sieht. Dieselbe Funktion in TypeScript:

TypeScript
function bruttopreis(netto: number, steuersatz: number): number {
  return netto * (1 + steuersatz);
}

console.log(bruttopreis(100, 0.19));
console.log(bruttopreis("100", 0.19));

Prüfst du die Datei mit tsc --noEmit --strict preis.ts, meldet der Compiler:

Text
preis.ts(6,25): error TS2345: Argument of type 'string' is not assignable to parameter of type 'number'.

--noEmit heißt, dass nur geprüft und keine Datei erzeugt wird; Exit-Code 2 zeigt den Fehler an. Die Typen im Beispiel (: number) sind die Annotationen, die der Compiler gegen jeden Aufruf prüft.

Ein Typfehler ist kein Laufzeitfehler

Ein wichtiger Punkt für das Verständnis: Der Compilerfehler stoppt die Ausführung nicht von selbst. Node.js kann TypeScript-Dateien ausführen, indem es die Typen entfernt, ohne sie zu prüfen. In diesem Lauf mit Node.js 26.3.0 gab dieselbe Datei trotz des Typfehlers aus:

Text
119
119

Die Node.js-Dokumentation sagt es ausdrücklich: Node ersetzt TypeScript-Syntax durch Leerraum, eine Typprüfung findet nicht statt. Die Prüfung gehört deshalb in deinen Workflow: im Editor, per tsc --noEmit lokal und in der CI.

Was TypeScript nicht prüft

Typen beschreiben, was du erwartest, nicht was zur Laufzeit ankommt. Ein Typ-Cast (as) behauptet etwas, ohne es zu prüfen:

TypeScript
const antwort = JSON.parse('{"preis":"12"}') as { preis: number };
console.log(typeof antwort.preis, antwort.preis + 1);
Text
string 121

Der Compiler akzeptiert das ohne Fehler (tsc Exit-Code 0), obwohl preis tatsächlich ein String ist; "12" + 1 ergibt "121". Bei Daten aus APIs, Formularen oder Dateien ist die Typangabe also eine Behauptung. Wer zur Laufzeit Sicherheit braucht, muss die Daten selbst prüfen (oder dafür eine Validierungsbibliothek einsetzen).

unknown statt any

TypeScript kennt zwei „beliebig“-Typen mit sehr unterschiedlichem Verhalten. any schaltet die Typprüfung für einen Wert ab. unknown heißt „Typ nicht bekannt“: Du darfst den Wert erst verwenden, nachdem du seinen Typ geprüft hast.

TypeScript
interface Kontakt {
  email: string;
  name?: string;
}

type Status = "neu" | "aktiv" | "abgemeldet";

function lies(daten: unknown): Kontakt {
  if (typeof daten === "object" && daten !== null && "email" in daten
      && typeof daten.email === "string") {
    return { email: daten.email };
  }
  throw new Error("Kein gültiger Kontakt");
}

const roh: unknown = JSON.parse('{"email":"anna@example.com"}');
const kontakt = lies(roh);
console.log(kontakt.email.toUpperCase());

const beliebig: any = JSON.parse('{"email":42}');
console.log(beliebig.email.toUpperCase()); // any: kein Compilerfehler

const zustand: Status = "aktiv";
console.log(zustand);

Dieses Beispiel kompiliert mit --strict ohne Fehler. Beim Ausführen mit Node.js gibt die erste Zeile ANNA@EXAMPLE.COM aus, die zweite wirft TypeError: beliebig.email.toUpperCase is not a function. Mit any hat der Compiler den Fehler nicht gesehen, mit unknown hätte er dich zur Prüfung gezwungen. Den Zugriff auf einen unknown-Wert ohne Prüfung lehnt der Compiler ab:

TypeScript
type Status = "neu" | "aktiv";
const roh: unknown = JSON.parse('{"email":"anna@example.com"}');
console.log(roh.email);
const zustand: Status = "inaktiv";
Text
unknown2.ts(3,13): error TS18046: 'roh' is of type 'unknown'.
unknown2.ts(4,7): error TS2820: Type '"inaktiv"' is not assignable to type 'Status'. Did you mean '"aktiv"'?

Die zweite Meldung zeigt nebenbei einen Vorteil von Union-Typen aus Stringliteralenn: Tippfehler in festen Werten wie Status-Namen fallen sofort auf.

Interface oder type?

Beide beschreiben die Form von Objekten. Die TypeScript-Dokumentation empfiehlt, standardmäßig interface zu verwenden und type zu nehmen, wenn du dessen Fähigkeiten brauchst, etwa Unions oder Literaltypen:

TypeScript
interface A { id: number }
interface B extends A { name: string }
type C = A & { name: string };
type Id = number | string;
const b: B = { id: 1, name: "x" };
const c: C = b;
const i: Id = "7";
console.log(b, c, i);

Der Code kompiliert mit --strict ohne Fehler und gibt { id: 1, name: 'x' } { id: 1, name: 'x' } 7 aus. Typisierung ist außerdem strukturell: Es zählt die Form eines Objekts, nicht der Name seines Typs. Ein Objekt mit den passenden Eigenschaften passt, auch wenn es nie als Point oder Kontakt deklariert wurde.

strict: der wichtigste Schalter

Die Option strict in der tsconfig.json aktiviert laut TSConfig-Referenz die „strict mode family“: unter anderem noImplicitAny, strictNullChecks, strictFunctionTypes und useUnknownInCatchVariables. noImplicitAny sorgt dafür, dass der Compiler einen Fehler meldet, wo er sonst stillschweigend any angenommen hätte. Die Dokumentation weist außerdem darauf hin, dass künftige TypeScript-Versionen unter diesem Schalter weitere Prüfungen ergänzen können, ein Update also neue Fehler zeigen kann.

Für neue Projekte ist strict: true deshalb der sinnvolle Ausgangspunkt. Bei einer Migration kannst du einzelne strikte Prüfungen vorübergehend ausschalten und später aktivieren.

Der Umstieg: Schritt für Schritt

Du musst nicht alles auf einmal umschreiben. Die TypeScript-Dokumentation beschreibt eine Skala von „nur JavaScript“ bis „TypeScript mit strict“, und jede Stufe ist für sich nutzbar:

  1. Editor-Unterstützung ohne Änderung. Editoren, die TypeScript nutzen, liefern auch für JavaScript Autovervollständigung und Umbenennen.
  2. Typen per JSDoc. Du annotierst in Kommentaren, ohne die Dateiendung zu ändern.
  3. // @ts-check als erste Zeile einer .js-Datei schaltet die Prüfung für diese Datei ein. Für viele Dateien reicht eine jsconfig.json mit checkJs.
  4. .ts-Dateien. Du benennst Dateien um, sobald sie bereit sind.
  5. strict. Zum Schluss ziehst du die Strenge an.

Die JSDoc-Variante der Funktion von oben sieht so aus:

JavaScript
// @ts-check

/**
 * @param {number} netto
 * @param {number} steuersatz
 * @returns {number}
 */
function bruttopreis(netto, steuersatz) {
  return netto * (1 + steuersatz);
}

console.log(bruttopreis("100", 0.19));
Text
legacy.js(12,25): error TS2345: Argument of type 'string' is not assignable to parameter of type 'number'.

Das ist derselbe Fehler wie in der .ts-Datei, nur ohne Build-Schritt und ohne Umbenennen. Projektweit steuerst du das über allowJs (JavaScript-Dateien in den Compiler einbeziehen) und checkJs (sie auch prüfen); in diesem Beispiel lief tsc --noEmit --allowJs --checkJs --strict. Für unlösbare Stellen gibt es // @ts-expect-error: Es unterdrückt den Fehler der Folgezeile und meldet sich selbst, sobald dort kein Fehler mehr vorliegt.

TypeScript direkt in Node.js ausführen

Früher brauchte jede TypeScript-Datei vor der Ausführung einen Kompilierschritt. Nach der Node.js-Dokumentation (Stand 28.10.2026) führt Node.js TypeScript-Dateien, die nur „löschbare“ Syntax enthalten, ohne Flag aus; das gilt seit v23.6.0 beziehungsweise v22.18.0. Das Feature ist seit v25.2.0 und v24.12.0 als „Stable“ eingestuft. Node entfernt dabei nur die Typen (Type Stripping) und prüft nichts.

Es gibt Grenzen. Alles, was neues JavaScript erzeugen müsste, scheitert: enum-Deklarationen, Namespaces mit Laufzeitcode und Parameter-Properties. Der Lauf bestätigt das:

Text
SyntaxError [ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX]: TypeScript enum is not supported in strip-only mode

Dazu kommen laut Dokumentation: Importe brauchen die Dateiendung (./datei.ts), Typen müssen mit import type importiert werden, tsconfig.json wird zur Laufzeit ignoriert und .tsx wird nicht unterstützt. Damit der Compiler dieselben Einschränkungen kennt, empfiehlt die Node-Dokumentation erasableSyntaxOnly in der tsconfig.json:

JSON
{
  "compilerOptions": {
    "noEmit": true,
    "target": "esnext",
    "module": "nodenext",
    "rewriteRelativeImportExtensions": true,
    "erasableSyntaxOnly": true,
    "verbatimModuleSyntax": true
  }
}

Mit dieser Option meldet tsc für das Enum oben error TS1294: This syntax is not allowed when 'erasableSyntaxOnly' is enabled. Für Projekte mit JSX, Enums oder Bundler brauchst du weiterhin einen Build-Schritt oder ein Werkzeug wie tsx.

Vergleich auf einen Blick

KriteriumJavaScriptTypeScript
Typendynamisch, zur Laufzeitstatisch, zur Entwicklungszeit
Fehlererkennungbeim Ausführenbeim Kompilieren bzw. im Editor
Laufzeitprüfungnur, was du selbst schreibstkeine zusätzliche – Typen werden entfernt
Build-Schrittneinin der Regel ja (oder Node.js Type Stripping mit Einschränkungen)
Editor-Unterstützunggut, per Inferenzbesser, durch ausdrückliche Typen
EinstiegsofortJavaScript-Kenntnisse plus Typsystem
Umstieg–schrittweise möglich (JSDoc, allowJs)
Konfigurationkeine nötigtsconfig.json

Wann JavaScript reicht

TypeScript lohnt sich nicht überall. JavaScript ist oft die bessere Wahl bei kleinen Skripten, Prototypen, einzelnen Seiten mit etwas Interaktivität oder wenn du gerade erst programmieren lernst. Dann ist jede zusätzliche Konfiguration vor allem Ballast. Wer später wächst, kann – wie oben gezeigt – mit JSDoc und // @ts-check anfangen und braucht kein Neuschreiben.

TypeScript spielt seine Stärken aus, wenn mehrere Personen am selben Code arbeiten, Funktionen viele Aufrufer haben, Daten aus verschiedenen Quellen zusammenlaufen (etwa API-Schnittstellen) oder du Code über Jahre pflegst. Auch bei Frameworks begegnet dir TypeScript häufig; wie das in React und Vue aussieht, behandelt der React-vs.-Vue-Vergleich.

Dein nächster Schritt

Nimm eine einzelne JavaScript-Datei aus einem eigenen Projekt, schreibe // @ts-check in die erste Zeile und öffne sie in einem Editor mit TypeScript-Unterstützung. Behebe die ersten Meldungen mit JSDoc-Typen. Wenn dir das nützt, lege eine tsconfig.json mit "strict": true an und benenne die erste Datei in .ts um. Welche Sprache für dein Ziel grundsätzlich passt, klärt die Entscheidungshilfe zur Programmiersprache, und Änderungen am Code sicherst du am besten mit den Git-Befehlen ab.

Häufige Fragen

Ist TypeScript schneller als JavaScript?

Nein. Die Typen werden vor der Ausführung entfernt, es läuft normales JavaScript. Der Nutzen liegt in früher erkannten Fehlern und besserer Editor-Unterstützung, nicht in der Laufzeit.

Muss ich JavaScript können, bevor ich TypeScript lerne?

Ja, zumindest die Grundlagen. TypeScript ergänzt JavaScript um Typen; Sprachmittel wie Funktionen, Objekte und async/await sind dieselben.

Quellen

  1. TypeScript for JavaScript Programmers (TypeScript in 5 minutes) typescriptlang.org (Microsoft) · abgerufen am 28. Oktober 2026
  2. TSConfig Reference: strict, noImplicitAny typescriptlang.org (Microsoft) · abgerufen am 28. Oktober 2026
  3. Migrating from JavaScript typescriptlang.org (Microsoft) · abgerufen am 28. Oktober 2026
  4. Modules: TypeScript (Running TypeScript Natively) Node.js Documentation · abgerufen am 28. Oktober 2026