Programmiersprache

TypeScript

TypeScript ergänzt JavaScript um statische Typen und fängt Bugs ab, bevor sie die Produktion erreichen. Hier erfährst du, was es ist, wie es funktioniert und wie du heute damit startest.

beginner20 min readUpdated 15. Sept. 2026
typescript
function greet(name: string): string {
  return `Hello, ${name}!`;
}

const message = greet("TypeScript");
console.log(message);
Erstellt
2012, von Microsoft
Typ
Statisch typisierter Superset von JavaScript
Kompiliert zu
Purem JavaScript
Standard
ECMAScript mit Typ-Erweiterungen
Dateiendung
.ts / .tsx
Aktuelle Version
TypeScript 5.x

Warum es wichtig ist

Warum TypeScript wichtig ist

Bugs frühzeitig finden

Typfehler werden zur Kompilierzeit abgefangen, nicht in der Produktion. Du findest Fehler, bevor dein Code überhaupt ausgeführt wird.

Bessere Editor-Experience

Autocomplete, Inline-Dokumentation und Refactoring-Tools funktionieren, weil der Editor die Struktur deines Codes versteht.

Selbstdokumentierender Code

Typen beschreiben, was Funktionen erwarten und zurückgeben. Die Signatur verrät dir genau, wie sie zu verwenden ist.

Das Gesamtbild

Die drei Ebenen von TypeScript

TypeScript ist eine Sprache, ein Compiler und ein Typsystem. Zu wissen, auf welcher Ebene man arbeitet, macht das Debugging wesentlich einfacher.

TypeScript

Die Sprache

JavaScript plus Typ-Annotationen, Interfaces, Generics und fortgeschrittene Typ-Operationen.

Der Compiler

tsc

Konvertiert TypeScript in JavaScript, prüft Typen und gibt sauberen Output für jedes Ziel aus.

Das Ökosystem

Tools & Bibliotheken

IDE-Support, Deklarationsdateien, Strict Mode und Frameworks, in denen TypeScript bereits integriert ist.

TypeScript auf einen Blick

Was die Sprache dir bietet

Primitive Typen

string, number, boolean, null, undefined, symbol, bigint.

Objekt-Typen

Interfaces, Type Aliases, Klassen und Arrays.

Unions & Intersections

Kombiniere Typen mit | (oder) und & (und), um flexible Daten zu modellieren.

Type Narrowing

Nutze typeof, in und Diskriminanten, damit TypeScript Typen innerhalb von Bedingungen eingrenzen kann.

Generics

Schreibe wiederverwendbare Funktionen und Typen, die mit jedem Typ funktionieren und dennoch typsicher bleiben.

Utility Types

Integrierte Transformationen wie Partial, Pick und Record, die bestehende Typen modifizieren.

Eine kurze Geschichte

Von optionalen Annotationen zum Industriestandard

  1. 2012

    TypeScript Launch

    Microsoft veröffentlicht TypeScript als optionale Typ-Ebene für JavaScript.

    12
  2. 2016

    Angular setzt auf TypeScript

    Angular übernimmt TypeScript als primäre Sprache und treibt die Adaption im gesamten Ökosystem voran.

    16
  3. 2018

    TypeScript 3.0

    Project References, der unknown-Typ und Tuples machen die Sprache mächtiger.

    18
  4. 2020

    Variadic Tuples

    TypeScript 4.0 bringt variadic tuple types und benannte Tuple-Elemente.

    20
  5. 2023

    Standardisierung von Decorators

    TC39 Decorators landen in TypeScript 5.0 und richten sich nach dem ECMAScript-Vorschlag.

    23
  6. Heute

    Die Standardwahl

    Die meisten neuen JavaScript-Projekte starten mit TypeScript. Frameworks liefern es bereits integriert aus.

    Heute

Der vollständige Leitfaden

TypeScript: Alles was Sie wissen müssen

Was ist TypeScript?

TypeScript ist ein statically typed Superset von JavaScript, das optionale Typ-Annotationen hinzufügt, die zur Kompilierzeit geprüft werden. Es wird in reines JavaScript kompiliert und läuft daher überall dort, wo JavaScript läuft – in Browsern, Node.js, Deno und Bun.

TypeScript wurde 2012 von Microsoft entwickelt und ist mittlerweile die Standardwahl für professionelle JavaScript-Projekte. Der Grund ist simpel: Es findet Bugs, bevor Ihr Code überhaupt ausgeführt wird. Ein Tippfehler in einem Eigenschaftsnamen, ein fehlendes Argument, ein falscher Rückgabetyp – TypeScript markiert diese Fehler bereits beim Kompilieren, anstatt sie zu Abstürzen in der Production werden zu lassen.

Am besten betrachten Sie TypeScript als JavaScript mit einem Sicherheitsnetz. Sie schreiben dieselbe Sprache, die Sie bereits kennen, können aber die Struktur Ihrer Daten und die Verträge Ihrer Funktionen beschreiben. Der Compiler prüft, ob alles zusammenpasst, und entfernt anschließend die Typen, sodass sauberes JavaScript übrig bleibt.

Warum TypeScript wichtig ist

JavaScript ist dynamisch typisiert. Das bedeutet, dass man jedem beliebigen Funktion jeden beliebigen Wert übergeben kann und Fehler erst dann auftreten, wenn der Code tatsächlich ausgeführt wird. In einem kleinen Skript ist das völlig in Ordnung. In einer großen Anwendung mit Dutzenden von Entwicklern ist dies jedoch ein Rezept für Bugs in der Production.

TypeScript löst dieses Problem, indem es die Typen prüft, bevor der Code ausgeführt wird:

  • Bugs zur Compile-Zeit finden — Wenn Sie eine Property falsch schreiben, den falschen Argumenttyp übergeben oder vergessen, null zu behandeln, weist TypeScript Sie sofort darauf hin.
  • Bessere Editor-Experience — Autocomplete funktioniert reibungslos, da der Editor genau weiß, welche Properties ein Objekt besitzt. Refactoring ist sicher, weil der Compiler jede Stelle findet, die aktualisiert werden muss.
  • Selbstdokumentierender Code — Funktionssignaturen beschreiben exakt, was sie erwarten und zurückgeben. Die Typen verraten Ihnen, wie der Code zu verwenden ist, ohne dass Sie die Implementierung lesen müssen.
  • Sichereres Refactoring — Ändern Sie einen Typ, und der Compiler markiert jede Stelle, an der dadurch etwas kaputtgeht. Kein mühsames Durchsuchen der Codebase nach Runtime-Fehlern mehr.
  • Progressive Einführung — Sie können TypeScript schrittweise in ein bestehendes JavaScript-Projekt integrieren, Datei für Datei.

TypeScript verursacht keinen Runtime-Overhead. Die Typen werden während der Kompilierung entfernt, sodass das JavaScript, das an die Nutzer ausgeliefert wird, identisch ist – unabhängig davon, ob es in TypeScript geschrieben wurde oder nicht.

So funktioniert TypeScript

TypeScript-Code befindet sich in .ts-Dateien. Der TypeScript-Compiler (tsc) liest diese Dateien, prüft die Typen und gibt .js-Dateien aus, in denen die Typannotationen entfernt wurden.

// app.ts
function add(a: number, b: number): number {
  return a + b;
}

const result = add(1, 2); // result is number

Der Compiler prüft, ob a und b Zahlen sind, ob die Funktion eine Zahl zurückgibt und ob result eine Zahl erhält. Wenn etwas nicht übereinstimmt, erhalten Sie einen Fehler zur Kompilierzeit. Das ausgegebene JavaScript sieht einfach so aus:

function add(a, b) {
  return a + b;
}
const result = add(1, 2);

Keine Typen, kein Overhead. Die Sicherheit bestand nur im Build-Schritt.

Typ-Annotationen

Typ-Annotationen beschreiben den Typ einer Variable, eines Parameters oder eines Rückgabewerts:

// primitives
let name: string = "Ada";
let age: number = 36;
let active: boolean = true;

// arrays
let skills: string[] = ["HTML", "CSS"];
let scores: Array<number> = [95, 87, 92];

// function parameters and return
function greet(name: string): string {
  return `Hello, ${name}!`;
}

Annotationen sind nicht immer erforderlich. TypeScript leitet Typen aus dem Wert ab (Type Inference):

let count = 0;          // inferred as number
const items = ["a"];    // inferred as string[]

Bevorzuge die Typ-Ableitung, wenn der Typ offensichtlich ist. Nutze Annotationen, wenn der Typ nicht eindeutig aus dem Wert hervorgeht, wenn du einen allgemeineren Typ benötigst oder wenn die Standard-Ableitung zu spezifisch ist.

Typinferenz

TypeScript leitet Typen automatisch aus dem Kontext ab:

// from assignment
let x = 5; // number

// from return value
function doubling(n: number) {
  return n * 2; // inferred as number
}

// from context
const numbers = [1, 2, 3]; // number[]
const doubled = numbers.map(n => n * 2); // number[]

Inferenz bedeutet, dass du weniger Code schreiben musst, während du die gleiche Sicherheit erhältst. Die meisten TypeScript-Codes verlassen sich stark auf die Inferenz – nutze explizite Typannotationen nur dann, wenn sie für mehr Klarheit sorgen.

Union Types

Ein Union Type gibt an, dass ein Wert einer von mehreren Typen angehören kann:

type Status = "loading" | "success" | "error";

function handleStatus(status: Status) {
  if (status === "loading") {
    // TypeScript knows status is "loading" here
  }
}

let value: string | number;
value = "hello";
value = 42;

Unions eignen sich hervorragend zur Modellierung von realen Daten. API-Antworten können beispielsweise erfolgreich sein oder fehlschlagen. Ein Feld könnte ein String oder null sein. Eine Funktion könnte verschiedene Input-Typen akzeptieren.

Type Narrowing

TypeScript schränkt Union Types innerhalb von Conditionals ein (Type Narrowing):

function process(value: string | number) {
  if (typeof value === "string") {
    // TypeScript knows value is string here
    return value.toUpperCase();
  }
  // TypeScript knows value is number here
  return value.toFixed(2);
}

Narrowing funktioniert auch mit:

  • in Operator — prüft, ob eine Eigenschaft existiert
  • instanceof — prüft Klasseninstanzen
  • Gleichheitsprüfungen — === und !==
  • Discriminated Unions — prüft eine gemeinsame Tag-Eigenschaft
type Circle = { kind: "circle"; radius: number };
type Square = { kind: "square"; side: number };
type Shape = Circle | Square;

function area(shape: Shape) {
  switch (shape.kind) {
    case "circle":
      return Math.PI * shape.radius ** 2;
    case "square":
      return shape.side ** 2;
  }
}

Discriminated Unions gehören zu den mächtigsten Patterns in TypeScript. Die kind Eigenschaft teilt TypeScript genau mit, mit welcher Variante man es gerade zu tun hat.

Interfaces und Type Aliases

Ein Interface beschreibt die Struktur eines Objekts – seine Eigenschaften, deren Typen und ob diese erforderlich sind. Ein Type Alias gibt jedem beliebigen Typ einen Namen, einschließlich Unions, Tuples, Primitiven und Funktionstypen.

// Interface
interface User {
  id: number;
  name: string;
  email: string;
}

// Type alias with the same shape
type User = {
  id: number;
  name: string;
  email: string;
};

Bei einfachen Objektstrukturen sind beide austauschbar. Die Unterschiede werden deutlich, wenn man Typen kombiniert oder erweitert.

Optionale und Readonly-Eigenschaften

interface User {
  id: number;
  name: string;
  email?: string;      // optional — string | undefined
  readonly createdAt: Date;
}

const user: User = { id: 1, name: "Ada", createdAt: new Date() };
user.createdAt = new Date(); // Error: cannot assign to a readonly property

Interfaces erweitern

Interfaces erben von anderen Interfaces mittels extends, wobei auch mehrere gleichzeitig erweitert werden können:

interface Timestamped {
  createdAt: Date;
  updatedAt: Date;
}

interface User extends Timestamped {
  id: number;
  name: string;
}

Type Aliases für Unions, Tuples und Funktionen

Type Aliases spielen ihre Stärken aus, wenn der Typ kein einfaches Objekt ist:

type ID = string | number;
type Status = "idle" | "loading" | "success" | "error";
type Pair = [string, number];
type Callback = (data: unknown) => void;

Intersection Types

Kombiniere Typen mit &:

type User = { id: number; name: string };
type WithTimestamp = { createdAt: Date };

type TimestampedUser = User & WithTimestamp;

Interface oder Type?

Verwende Interfaces für Objektstrukturen, die erweitert oder von Klassen implementiert werden können. Verwende Type Aliases für Unions, Intersections, Tuples und Funktionstypen. Entscheide dich für eine Konvention bei Objektstrukturen und bleibe konsistent.

Generics

Generics sind Typ-Parameter: Sie ermöglichen es einer Funktion, einem Interface oder einer Klasse, mit jedem beliebigen Typ zu arbeiten, während die Typsicherheit erhalten bleibt. Anstatt einen Typ fest zu codieren oder auf any zurückzugreifen, übergibt man eine Typvariable, die erst beim Aufruf festgelegt wird.

function first<T>(items: T[]): T | undefined {
  return items[0];
}

const num = first([1, 2, 3]);    // number
const str = first(["a", "b"]);   // string

Ohne Generics müsste man entweder any verwenden und damit Typinformationen verlieren oder für jeden Typ eine separate Funktion schreiben.

Generische Funktionen

Ein Typ-Parameter wird in spitzen Klammern deklariert. TypeScript leitet diesen normalerweise aus den Argumenten ab, sodass man ihn selten explizit übergeben muss:

function merge<T, U>(a: T, b: U): T & U {
  return { ...a, ...b };
}

const result = merge({ name: "Ada" }, { age: 36 });
// { name: string; age: number }

Generische Interfaces und Klassen

interface Response<T> {
  data: T;
  status: number;
}

interface User {
  id: number;
  name: string;
}

const response: Response<User> = {
  data: { id: 1, name: "Ada" },
  status: 200,
};

class Stack<T> {
  private items: T[] = [];
  push(item: T) { this.items.push(item); }
  pop(): T | undefined { return this.items.pop(); }
}

Constraints

Verwenden Sie extends, um einzuschränken, welche Typen ein Generic akzeptieren kann:

interface HasId {
  id: number;
}

function findById<T extends HasId>(items: T[], id: number): T | undefined {
  return items.find((item) => item.id === id);
}

Standard-Typ-Parameter

interface Paginated<T = unknown> {
  data: T[];
  total: number;
  page: number;
}

const response: Paginated = { data: [1, "a"], total: 10, page: 1 };

Generics werden zur Laufzeit entfernt (Type Erasure), sodass sie keinen Overhead verursachen – sie dienen lediglich dazu, den Compiler und Ihren Editor intelligenter zu machen.

Die tsconfig.json

Die Datei tsconfig.json konfiguriert den TypeScript-Compiler:

{
  "compilerOptions": {
    "target": "ES2020",
    "module": "ESNext",
    "strict": true,
    "outDir": "./dist",
    "rootDir": "./src",
    "esModuleInterop": true,
    "skipLibCheck": true
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules"]
}

Wichtige Optionen:

  • target — welche JavaScript-Version ausgegeben werden soll (ES2020 ist ein guter Standardwert)
  • strict — aktiviert alle Flags für die strikte Typprüfung
  • module — das Modulsystem für die Ausgabe (ESNext für moderne Projekte)
  • outDir — das Zielverzeichnis für das kompilierte JavaScript
  • esModuleInterop — ermöglicht Default-Imports aus CommonJS-Modulen

Verwenden Sie für neue Projekte immer "strict": true. Dadurch werden mehr Bugs abgefangen, auch wenn der Code dadurch etwas expliziter geschrieben werden muss.

TypeScript installieren

# As a project dependency (recommended)
npm install --save-dev typescript

# Globally (not recommended for teams)
npm install -g typescript

Kompiliere dein Projekt:

# Using npx
npx tsc

# Or add a script to package.json
npm run build

Für die Entwicklung im Watch-Modus:

npx tsc --watch

Die meisten modernen Frameworks (Next.js, Vite, Astro) übernehmen die TypeScript-Kompilierung automatisch für dich – du musst tsc nicht manuell ausführen.

Häufige Fehler

  • any überall zu verwenden, was die Typprüfung deaktiviert.
  • Alles zu annotieren, obwohl die Typinferenz den Typ bereits kennt.
  • Den Strict Mode in der tsconfig nicht zu aktivieren.
  • TypeScript-Typen mit Runtime-Werten zu verwechseln – Typen werden zur Compile-Zeit entfernt.
  • Typen falsch zu importieren – verwenden Sie import type für reine Typ-Imports.
  • Compiler-Fehler zu ignorieren, anstatt sie zu beheben.
  • Keine @types-Pakete für JavaScript-Bibliotheken zu installieren.
  • Jede Eigenschaft eines Interfaces optional zu machen, wodurch notwendige Prüfungen verborgen bleiben.
  • any anstelle eines Generics zu verwenden, wodurch die Typinformationen verloren gehen, die man eigentlich behalten wollte.

Best Practices

  • Aktiviere den Strict Mode vom ersten Tag an.
  • Überlass die Arbeit der Typinferenz – nutze explizite Annotationen nur dort, wo sie einen Mehrwert bieten.
  • Verwende unknown anstelle von any, wenn der Typ unbekannt ist.
  • Bevorzuge Interfaces für Objektstrukturen, die möglicherweise erweitert werden.
  • Nutze Discriminated Unions für State Machines und variantenbasierte Daten.
  • Halte Typdefinitionen nah an der Stelle, an der sie verwendet werden.
  • Nutze import type für reine Typ-Imports, um den Runtime-Code schlank zu halten.
  • Betrachte den Compiler als Teammitglied, nicht als Hindernis.

Was du als Nächstes lernen solltest

Du beherrschst nun die TypeScript-Grundlagen: Annotations, Inference, Unions, Narrowing, Interfaces, Type Aliases und Generics. Der nächste logische Schritt sind Advanced Types, um Utility Types, Conditional Types und Mapped Types kennenzulernen. Such dir ein Projekt aus, refactore etwas Code und vertiefe dein Wissen durch die Praxis.

Typ-Annotationen

Explizite Annotationen helfen, wenn der Typ nicht offensichtlich ist. Lass die Inference die Arbeit machen, wenn er es ist.

Bevorzugt
const count = 0;
const items: string[] = [];

function add(a: number, b: number) {
  return a + b;
}
Vermeiden
const count: number = 0;
const items: Array<string> = [];

function add(a: number, b: number): number {
  return a + b;
}

Verwendung von any

any deaktiviert die Typprüfung. Nutze unknown, wenn du den Typ nicht kennst, und grenze ihn anschließend ein.

Bevorzugt
function parse(input: unknown) {
  if (typeof input === "string") {
    return input.toUpperCase();
  }
  return String(input);
}
Vermeiden
function parse(input: any) {
  return input.toUpperCase();
}

Häufig gestellte Fragen

Häufig gestellte Fragen

Keep learning

Related topics from the roadmap.

$ Lernen Sie jetzt

Bereit, TypeScript zu lernen?

Unser interaktives Tutorial führt Sie Schritt für Schritt durch TypeScript — mit Quizzen und echtem Code, den Sie im Browser ausführen können.