Programming Language

Fortgeschrittene TypeScript-Typen

Utility Types, Conditional Types und Mapped Types ermöglichen es Ihnen, Typen präzise zu transformieren und zu komponieren. Hier erfahren Sie, wie sie funktionieren und wann man sie einsetzt.

intermediate16 min readUpdated 15. Sept. 2026
typescript
type Users = {
  ada: { age: 36 };
  bob: { age: 28 };
};

type UserNames = keyof Users;
type Ages = Users[UserNames];
Utility Types
Integrierte Typ-Transformationen (Partial, Pick, Record)
Conditional Types
T extends U ? X : Y
Mapped Types
Transformation von Eigenschaften eines bestehenden Typs
Template Literals
String-Manipulation auf Typ-Ebene
infer Keyword
Extraktion von Typen aus anderen Typen
Distributive Conditionals
Anwendung von Bedingungen auf jedes Union-Mitglied

Warum es wichtig ist

Warum fortgeschrittene Typen wichtig sind

Typ-Transformation

Erstellen Sie neue Typen aus bestehenden, ohne Definitionen zu duplizieren. Ändern, filtern und formen Sie Typen so um, dass sie Ihren Anforderungen entsprechen.

Präzise Kontrakte

Modellieren Sie komplexe Daten mit exakten Typen, die Fehler finden, die anderen entgehen. Discriminated Unions, Exact Types und Branded Types.

Weniger Redundanz

Leiten Sie Typen automatisch ab, anstatt parallele Definitionen zu pflegen. Eine einzige Quelle der Wahrheit, viele abgeleitete Typen.

Das Gesamtbild

Die drei Säulen der Typ-Transformation

Utility Types, Conditional Types und Mapped Types sind die Werkzeuge, mit denen Sie jeden beliebigen Typ aus bestehenden Typen erstellen können, ohne sich zu wiederholen.

Utility Types

Integrierte Transformationen

Partial, Required, Pick, Omit, Record, Extract, Exclude und andere Standardtypen, die bestehende Typen modifizieren.

Conditional Types

Logik auf Typ-Ebene

If/Else auf Typ-Ebene. T extends U ? X : Y ermöglicht es, Typen basierend auf Bedingungen zu verzweigen.

Mapped Types

Eigenschafts-Transformation

Iterieren Sie über die Keys eines Typs und transformieren Sie jede Eigenschaft. Die Grundlage der meisten Utility Types.

Fortgeschrittene Typen auf einen Blick

Was Sie bauen können

Partial<T>

Macht alle Eigenschaften optional.

Required<T>

Macht alle Eigenschaften erforderlich.

Pick<T, K>

Extrahiert eine Teilmenge von Eigenschaften.

Omit<T, K>

Entfernt spezifische Eigenschaften.

Record<K, V>

Erstellt einen Objekttyp mit Keys K und Werten V.

Extract<U>

Extrahiert Mitglieder einer Union, die einem Typ entsprechen.

Eine kurze Geschichte

Von einfachen Aliassen zur Programmierung auf Typ-Ebene

  1. 2016

    Utility Types erscheinen

    TypeScript 2.1 führt Mapped Types und integrierte Utilities wie Partial, Readonly und Pick ein.

    16
  2. 2018

    Conditional Types kommen

    TypeScript 2.8 fügt Conditional Types hinzu, was Logik auf Typ-Ebene und das infer Keyword ermöglicht.

    18
  3. 2019

    Rekursive Typen

    TypeScript 4.1 ermöglicht rekursive Conditional Types und Template Literal Types.

    19
  4. 2021

    Template Literal Types

    String-Manipulation auf Typ-Ebene eröffnet neue Möglichkeiten für das API-Design.

    21
  5. 2023

    Satisfies Operator

    TypeScript 4.9 fügt satisfies hinzu, um Werte zu validieren, ohne Literal-Typen zu verlieren.

    23
  6. Heute

    Programmierung auf Typ-Ebene

    Fortgeschrittene Typen sind essenziell für Bibliotheken, API-Clients und komplexe Anwendungszustände.

    Heute

Der vollständige Leitfaden

Fortgeschrittene TypeScript-Typen: Alles was Sie wissen müssen

Was sind fortgeschrittene TypeScript-Typen?

Fortgeschrittene Typen sind Werkzeuge, mit denen Sie Typen auf der Typ-Ebene transformieren, kombinieren und manipulieren können. Anstatt für jede Variation manuell parallele Typen zu definieren, leiten Sie diese automatisch aus bestehenden ab.

Während die Grundlagen Ihnen Typen liefern und Generics es ermöglichen, wiederverwendbaren Code zu schreiben, erlauben Ihnen fortgeschrittene Typen den Aufbau eines Typsystems, das Ihre Daten präzise beschreibt – und so Fehler abfängt, die einfachere Ansätze übersehen würden.

Die drei Säulen sind Utility Types (integrierte Transformationen), Conditional Types (Logik auf Typ-Ebene) und Mapped Types (Transformation von Eigenschaften). Zusammen bilden sie eine kleine Sprache für die Arbeit mit Typen.

Utility Types

TypeScript liefert integrierte Typen mit, mit denen andere Typen modifiziert werden können:

Partial und Required

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

// All properties optional
type UserUpdate = Partial<User>;
// { id?: number; name?: string; email?: string; }

// All properties required
type StrictUser = Required<Partial<User>>;
// { id: number; name: string; email: string; }

Pick und Omit

// Extract specific properties
type UserPreview = Pick<User, "id" | "name">;
// { id: number; name: string; }

// Remove specific properties
type UserWithoutEmail = Omit<User, "email">;
// { id: number; name: string; }

Record

// Create object type with specific keys and values
type Scores = Record<string, number>;
// { [key: string]: number }

const scores: Scores = { math: 95, science: 87 };

// With union keys
type StatusMap = Record<"idle" | "loading" | "done", string>;

Extract und Exclude

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

// Keep only matching members
type ActiveStatus = Extract<Status, "loading" | "success">;
// "loading" | "success"

// Remove matching members
type InactiveStatus = Exclude<Status, "loading" | "success">;
// "idle" | "error"

ReturnType und Parameters

function createUser(name: string, age: number) {
  return { name, age, id: Date.now() };
}

type User = ReturnType<typeof createUser>;
// { name: string; age: number; id: number }

type Args = Parameters<typeof createUser>;
// [string, number]

NonNullable

type Maybe = string | null | undefined;
type Definite = NonNullable<Maybe>;
// string

Mapped Types

Mapped Types iterieren über die Keys eines bestehenden Typs und transformieren jede Eigenschaft:

type ReadOnly<T> = {
  readonly [K in keyof T]: T[K];
};

type MutableUser = ReadOnly<User>;
// All properties are readonly

Modifier hinzufügen

// Add optional
type Optional<T> = {
  [K in keyof T]?: T[K];
};

// Remove optional
type Concrete<T> = {
  [K in keyof T]-?: T[K];
};

// Remove readonly
type Mutable<T> = {
  -readonly [K in keyof T]: T[K];
};

Die Operatoren - und + entfernen oder fügen Modifier während des Mappings hinzu.

Key-Remapping mit as

TypeScript 4.1+ ermöglicht es, Keys während des Mappings neu zuzuweisen (Remapping):

type Getters<T> = {
  [K in keyof T as `get${Capitalize<string & K>}`]: () => T[K];
};

type UserGetters = Getters<User>;
// { getId: () => number; getName: () => string; getEmail: () => string; }

Conditional Types

Conditional Types ermöglichen Logik auf Typ-Ebene:

type IsString<T> = T extends string ? true : false;

type A = IsString<"hello">; // true
type B = IsString<42>;      // false

Das infer-Keyword

infer extrahiert einen Typ aus einem anderen Typ:

type ReturnType<T> = T extends (...args: any[]) => infer R ? R : never;

type Fn = () => string;
type Result = ReturnType<Fn>; // string

Praktische Beispiele

// Extract array element type
type ElementOf<T> = T extends (infer E)[] ? E : never;
type Num = ElementOf<number[]>; // number

// Unwrap promises
type Awaited<T> = T extends Promise<infer U> ? Awaited<U> : T;
type Deep = Awaited<Promise<Promise<string>>>; // string

// Extract object property types
type ValuesOf<T> = T[keyof T];
type UserValues = ValuesOf<User>; // number | string

Distributive Conditional Types

Wenn ein Conditional Type über eine Union verteilt wird, wird jedes Mitglied einzeln geprüft:

type ToArray<T> = T extends any ? T[] : never;

type Result = ToArray<string | number>;
// string[] | number[] — not (string | number)[]

Dies ist nützlich für Typen wie Extract und Exclude, die Union-Mitglieder filtern.

Template Literal Types

Template Literal Types ermöglichen die Manipulation von Strings auf Typ-Ebene:

type EventName<T extends string> = `on${Capitalize<T>}`;

type ClickEvent = EventName<"click">; // "onClick"
type FocusEvent = EventName<"focus">; // "onFocus"

Mit Mapped Types

type EventHandlers<T extends string> = {
  [K in T as `on${Capitalize<K>}`]: () => void;
};

type MouseEvents = EventHandlers<"click" | "hover" | "focus">;
// { onClick: () => void; onHover: () => void; onFocus: () => void; }

String Pattern Matching

type CSSProperty = `${string}-${string}`;
type ValidCSS = CSSProperty; // any string with a hyphen

Discriminated Unions

Discriminated Unions sind das wichtigste fortgeschrittene Pattern zur Modellierung von Zuständen:

type RequestState<T> =
  | { status: "idle" }
  | { status: "loading" }
  | { status: "success"; data: T }
  | { status: "error"; error: string };

function handleState(state: RequestState<User>) {
  switch (state.status) {
    case "idle":
      return "Ready";
    case "loading":
      return "Loading...";
    case "success":
      return state.data.name; // TypeScript knows data exists
    case "error":
      return state.error;    // TypeScript knows error exists
  }
}

Die Eigenschaft status ist der Diskriminator. TypeScript verfeinert den Typ in jedem Branch automatisch.

Branded Types

Branded Types verhindern das Vermischen von Typen, die dieselbe Struktur aufweisen:

type UserId = string & { readonly __brand: unique symbol };
type OrderId = string & { readonly __brand: unique symbol };

function branded<T extends string>(value: T): T & { readonly __brand: unique symbol } {
  return value as any;
}

const userId = branded<UserId>("user-123");
const orderId = branded<OrderId>("order-456");

function getUser(id: UserId) { /* ... */ }
getUser(userId);  // OK
getUser(orderId); // Error: OrderId is not assignable to UserId

Best Practices

  • Verwende Utility Types anstatt Typdefinitionen manuell zu duplizieren.
  • Bevorzuge Discriminated Unions gegenüber Type Assertions für die Modellierung von Zuständen.
  • Erstelle Typen aus kleinen, kombinierbaren Bausteinen anstatt aus großen, monolithischen Definitionen.
  • Setze Conditional Types sparsam ein – wenn ein Typ schwer lesbar ist, vereinfache die API.
  • Nutze infer, um Typen aus Libraries zu extrahieren, anstatt Rückgabewerte manuell zu typisieren.
  • Behalte die Source of Truth an einem Ort und leite alles Weitere davon ab.
  • Verwende Branded Types, wenn du nominales Typing für IDs oder ähnliche Werte benötigst.
  • Betrachte komplexe Typen als letzten Ausweg – einfache Typen sind leichter zu warten.

Häufige Fehler

  • Over-Engineering von Typen, obwohl einfachere Lösungen ausreichen würden.
  • Erstellung tief verschachtelter Conditional Types, die unmöglich zu debuggen sind.
  • Verwendung von as Assertions anstelle von korrektem Type Narrowing.
  • Nichtnutzung von Utility Types, was zu duplizierten Typdefinitionen führt.
  • Typen zu komplex gestalten, im Verhältnis zum Nutzen, den sie bieten.
  • Vergessen, dass Typen zur Laufzeit entfernt werden – es gibt kein Runtime Type Checking.
  • Komplexe Typen nicht mit Kommentaren dokumentieren, die die Absicht erklären.

Was du als Nächstes lernen solltest

Du verstehst nun fortgeschrittene Typen: Utility Types, Conditional Types, Mapped Types und Template Literals. Der nächste logische Schritt ist TypeScript für die Grundlagen – Annotations, Inference, Interfaces und Generics. Such dir ein Projekt aus, refactore einige Typen und lass die Praxis deine Kenntnisse vertiefen.

Utility Types vs. manuelle Definitionen

Utility Types leiten sich aus bestehenden Typen ab und bewahren eine einzige Quelle der Wahrheit. Manuelle Definitionen führen zu Duplikaten und Inkonsistenzen.

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

type UserUpdate = Partial<User>;
type UserPreview = Pick<User, "id" | "name">;
Vermeiden
interface User {
  id: number;
  name: string;
  email: string;
}

interface UserUpdate {
  id?: number;
  name?: string;
  email?: string;
}

Discriminated Unions vs. Type Assertions

Discriminated Unions geben TypeScript genügend Informationen, um Typen automatisch einzugrenzen (Narrowing).

Bevorzugt
type Result =
  | { ok: true; data: User }
  | { ok: false; error: string };

function handle(result: Result) {
  if (result.ok) {
    console.log(result.data);
  }
}
Vermeiden
interface Result {
  ok: boolean;
  data?: User;
  error?: string;
}

function handle(result: Result) {
  const user = result.data as User;
}

Häufig gestellte Fragen

Häufig gestellte Fragen

Keep learning

Related topics from the roadmap.

$ Lernen Sie jetzt

Bereit, Advanced Types zu lernen?

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