TypeScript & Typsystem

TypeScript Advanced Types mit Claude Code 2026

Conditional Types, Mapped Types, Template Literal Types, Infer, Utility Types und Type Guards — der vollständige Guide für fortgeschrittene TypeScript-Entwickler.

📅 6. Mai 2026 ⏱ 12 min Lesezeit 📚 TypeScript 5.x

Conditional 1. Conditional Types — T extends U ? X : Y

Conditional Types sind das mächtigste Werkzeug im TypeScript-Typsystem. Sie erlauben es, Typen abhängig von Bedingungen zu berechnen — ähnlich wie ein ternärer Operator, aber auf Typ-Ebene. Claude Code nutzt sie täglich, um präzise API-Typen zu generieren.

Die Grundform lautet: T extends U ? X : Y. Ist T assignierbar zu U, ergibt der Typ X, sonst Y.

conditional-basics.ts TypeScript
// Grundform: T extends U ? X : Y
type IsString<T> = T extends string ? "yes" : "no";

type A = IsString<string>;   // "yes"
type B = IsString<number>;   // "no"

// NonNullable selbst implementieren
type MyNonNullable<T> = T extends null | undefined ? never : T;

type C = MyNonNullable<string | null>;       // string
type D = MyNonNullable<number | undefined>;   // number

Das infer-Schlüsselwort

infer erlaubt es, einen Typ innerhalb einer Conditional Type zu "extrahieren" und als Variable zu binden. Damit lassen sich komplexe Typen wie Rückgabetypen oder Promise-Werte herauslösen.

infer-keyword.ts TypeScript
// infer: Typ-Extraktion in Conditional Types
type UnwrapPromise<T> =
  T extends Promise<infer U> ? U : T;

type E = UnwrapPromise<Promise<string>>;  // string
type F = UnwrapPromise<number>;           // number (kein Promise)

// Erstes Element eines Tuple-Typs extrahieren
type Head<T extends any[]> =
  T extends [infer First, ...any[]] ? First : never;

type G = Head<[string, number, boolean]>;  // string

// Letztes Element (rekursiv)
type Last<T extends any[]> =
  T extends [...any[], infer L] ? L : never;

type H = Last<[string, number, boolean]>;  // boolean

Distributive Conditional Types

Wenn T ein Union-Typ ist, wird der Conditional Type über jeden Member verteilt. Dieses Verhalten heißt "Distributivität" und ist oft sehr nützlich — kann aber auch überraschend sein.

distributive.ts TypeScript
// Distributiv: wird über Union-Member verteilt
type ToArray<T> = T extends any ? T[] : never;

type I = ToArray<string | number>;
// Ergebnis: string[] | number[]  (distributiv!)
// NICHT: (string | number)[]

// Distributivität deaktivieren: in Tuple einwickeln
type ToArrayNonDist<T> = [T] extends [any] ? T[] : never;

type J = ToArrayNonDist<string | number>;
// Ergebnis: (string | number)[]

// Praktisch: Union-Member filtern
type Filter<T, U> = T extends U ? T : never;
type OnlyStrings = Filter<string | number | boolean, string>;
// Ergebnis: string
Claude Code Tipp Mit never in Conditional Types lassen sich Union-Member gezielt herausfiltern. Das ist die Basis für viele eingebaute Utility Types wie Extract<T, U> und Exclude<T, U>.

Mapped 2. Mapped Types — Readonly, Partial, Pick selbst bauen

Mapped Types transformieren jeden Key eines bestehenden Typs nach einer Regel. Die Syntax { [K in keyof T]: ... } ist das Herzstück aller eingebauten Utility Types in TypeScript. Wer sie versteht, kann jedes Framework-Typsystem lesen und erweitern.

mapped-basics.ts TypeScript
// Grundform: alle Properties transformieren
type Stringify<T> = {
  [K in keyof T]: string;
};

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

type StringUser = Stringify<User>;
// { id: string; name: string; active: string; }

// Readonly selbst bauen
type MyReadonly<T> = {
  readonly [K in keyof T]: T[K];
};

// Partial selbst bauen
type MyPartial<T> = {
  [K in keyof T]?: T[K];
};

// Required selbst bauen (-? entfernt optional)
type MyRequired<T> = {
  [K in keyof T]-?: T[K];
};
pick-record.ts TypeScript
// Pick selbst bauen: nur bestimmte Keys behalten
type MyPick<T, K extends keyof T> = {
  [P in K]: T[P];
};

type UserPreview = MyPick<User, "id" | "name">;
// { id: number; name: string; }

// Record selbst bauen
type MyRecord<K extends keyof any, V> = {
  [P in K]: V;
};

type Roles = MyRecord<"admin" | "user" | "guest", boolean>;
// { admin: boolean; user: boolean; guest: boolean; }

// Key-Remapping mit as (TypeScript 4.1+)
type Getters<T> = {
  [K in keyof T as `get${Capitalize<string & K>}`]: () => T[K];
};

type UserGetters = Getters<User>;
// { getId(): number; getName(): string; getActive(): boolean; }
Best Practice Key-Remapping mit as und Template Literal Types zusammen erlauben es, ganze API-Interfaces aus einem einzigen Interface zu generieren — z.B. Getter/Setter-Paare oder Event-Handler-Namen.

Template 3. Template Literal Types — EventName, Route-Pattern, CSS

Mit TypeScript 4.1 wurden Template Literal Types eingeführt. Sie erlauben es, Strings auf Typ-Ebene zu konstruieren und zu manipulieren — ein Game-Changer für typsichere Event-Systeme, API-Routen und CSS-Properties.

template-literal-basics.ts TypeScript
// Grundform: String-Kombinationen
type Direction = "top" | "right" | "bottom" | "left";
type MarginProperty = `margin-${Direction}`;
// "margin-top" | "margin-right" | "margin-bottom" | "margin-left"

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

type ClickEvent = EventName<"click">;    // "onClick"
type ChangeEvent = EventName<"change">;  // "onChange"

// Auto-generierte Event-Handler aus Interface
type Events = "click" | "focus" | "blur" | "submit";
type Handlers = {
  [E in Events as `on${Capitalize<E>}`]: (event: Event) => void;
};
// { onClick, onFocus, onBlur, onSubmit }
route-patterns.ts TypeScript
// Route-Pattern-Types — typsichere API-Routen
type RouteParam<T extends string> =
  T extends `${string}:${infer Param}/${infer Rest}`
    ? Param | RouteParam<`/${Rest}`>
    : T extends `${string}:${infer Param}`
    ? Param
    : never;

type Params = RouteParam<"/users/:userId/posts/:postId">;
// "userId" | "postId"

// String-Manipulationen mit eingebauten Utility Types
type Upper = Uppercase<"hello">;          // "HELLO"
type Lower = Lowercase<"WORLD">;          // "world"
type Cap   = Capitalize<"typescript">;    // "Typescript"
type UnCap = Uncapitalize<"TypeScript">;  // "typeScript"

// CSS-Property-Types
type CSSValue = string | number;
type CSSProp = "color" | "background" | "padding" | "margin";
type CSSVarName<T extends string> = `--${T}`;
type CSSVars = CSSVarName<CSSProp>;
// "--color" | "--background" | "--padding" | "--margin"
Achtung: Complexity Template Literal Types können bei sehr großen String-Unions zu langen Compile-Zeiten führen. Für mehr als 50 Kombinationen besser auf Runtime-Validierung (z.B. Zod) setzen.

Utility 4. Utility Types tief verstehen — ReturnType, Awaited, ConstructorParameters

TypeScripts eingebaute Utility Types sind oft bekannt, aber selten vollständig verstanden. Wer weiß, wie sie intern funktionieren, kann sie kombinieren, erweitern und eigene Type-Bibliotheken bauen.

return-type.ts TypeScript
// ReturnType — Rückgabetyp einer Funktion extrahieren
function createUser(name: string, age: number) {
  return { id: Math.random(), name, age, createdAt: new Date() };
}

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

// Parameters — Parameter-Typen als Tuple extrahieren
type CreateUserParams = Parameters<typeof createUser>;
// [name: string, age: number]

// InstanceType — Typ einer Klassen-Instanz
class ApiClient {
  constructor(private baseUrl: string) {}
  get<T>(path: string): Promise<T> { return fetch(this.baseUrl + path).then(r => r.json()); }
}

type Client = InstanceType<typeof ApiClient>;
// ApiClient (Instanz-Typ)

// ConstructorParameters — Konstruktor-Parameter extrahieren
type ClientArgs = ConstructorParameters<typeof ApiClient>;
// [baseUrl: string]
awaited-type.ts TypeScript
// Awaited — rekursiv Promises auspacken (TS 4.5+)
type A = Awaited<Promise<string>>;                  // string
type B = Awaited<Promise<Promise<number>>>;           // number
type C = Awaited<boolean | Promise<string>>;          // boolean | string

// Praktisch: async-Funktions-Rückgabetyp
async function fetchUsers(): Promise<User[]> {
  return [];
}

type FetchResult = Awaited<ReturnType<typeof fetchUsers>>;
// User[]  (Promise wurde ausgepackt)

// Omit, Pick, Exclude, Extract kombinieren
type PublicUser = Omit<User, "password" | "secretKey">;
type StringKeys = Extract<keyof User, string>;
type NoActive = Exclude<keyof User, "active">;

Guard 5. Discriminated Unions und Type Guards

Discriminated Unions sind das bevorzugte Pattern in TypeScript, um mit verschiedenen Zuständen oder Varianten typsicher umzugehen. In Kombination mit Type Guards und dem never-Typ für Exhaustiveness-Checking entstehen robuste Zustandsmaschinen.

discriminated-unions.ts TypeScript
// Discriminated Union: gemeinsames Discriminant-Feld
type LoadingState = { status: "loading" };
type SuccessState = { status: "success"; data: User[] };
type ErrorState   = { status: "error";   message: string };

type AsyncState = LoadingState | SuccessState | ErrorState;

// Exhaustiveness-Checking mit never
function assertNever(x: never): never {
  throw new Error(`Unhandled case: ${JSON.stringify(x)}`);
}

function renderState(state: AsyncState): string {
  switch (state.status) {
    case "loading": return "Laden...";
    case "success": return `${state.data.length} Nutzer geladen`;
    case "error":   return `Fehler: ${state.message}`;
    default: return assertNever(state); // Compile-Fehler wenn Case fehlt!
  }
}
type-guards.ts TypeScript
// User-Defined Type Guard mit "is"
function isUser(value: unknown): value is User {
  return (
    typeof value === "object" &&
    value !== null &&
    "id" in value &&
    "name" in value
  );
}

// instanceof Guard
function handleError(err: unknown): string {
  if (err instanceof Error) return err.message;
  if (typeof err === "string") return err;
  return "Unbekannter Fehler";
}

// "in"-Operator als Type Guard
type Cat = { meow(): void };
type Dog = { bark(): void };

function speak(animal: Cat | Dog) {
  if ("meow" in animal) {
    animal.meow(); // TypeScript kennt jetzt: Cat
  } else {
    animal.bark(); // TypeScript kennt jetzt: Dog
  }
}

// Assertion Function (TS 3.7+)
function assertIsString(val: unknown): asserts val is string {
  if (typeof val !== "string") throw new Error("Not a string!");
}
Pattern Matching Simulation TypeScript hat kein natives Pattern Matching (wie Rust oder Haskell), aber Discriminated Unions + Exhaustiveness Checking mit never kommen dem sehr nahe — besonders in Kombination mit dem neuen satisfies-Operator (TS 4.9+).

Infer 6. Infer und rekursive Types — DeepPartial, Flatten, UnpackPromise

Rekursive Typen und infer zusammen sind das fortgeschrittenste Werkzeug im TypeScript-Arsenal. Sie erlauben es, beliebig tief verschachtelte Datenstrukturen auf Typ-Ebene zu transformieren — ohne Runtime-Overhead.

deep-partial.ts TypeScript
// DeepPartial — alle Properties rekursiv optional
type DeepPartial<T> = T extends object
  ? { [K in keyof T]?: DeepPartial<T[K]> }
  : T;

interface Config {
  server: { host: string; port: number; ssl: boolean };
  db: { url: string; pool: { min: number; max: number } };
}

type PartialConfig = DeepPartial<Config>;
// Alle verschachtelten Felder optional — perfekt für Overrides

// DeepReadonly — alle Properties rekursiv readonly
type DeepReadonly<T> = T extends object
  ? { readonly [K in keyof T]: DeepReadonly<T[K]> }
  : T;
flatten-unpack.ts TypeScript
// Flatten — verschachtelte Arrays auspacken
type Flatten<T> =
  T extends Array<infer Item>
    ? Flatten<Item>
    : T;

type F1 = Flatten<string[][]>;        // string
type F2 = Flatten<number[][][]>;      // number
type F3 = Flatten<boolean>;          // boolean (kein Array)

// UnpackPromise — tief verschachtelte Promises auspacken
type UnpackPromise<T> =
  T extends Promise<infer U>
    ? UnpackPromise<U>
    : T;

type P1 = UnpackPromise<Promise<Promise<string>>>;  // string

// Rekursiver Mapped Type: alle Werte stringifizieren
type DeepStringify<T> = {
  [K in keyof T]: T[K] extends object
    ? DeepStringify<T[K]>
    : string;
};
advanced-infer.ts TypeScript
// Tuple-Länge zur Compile-Zeit
type TupleLength<T extends any[]> = T["length"];
type Len = TupleLength<[string, number, boolean]>;  // 3

// Alle Keys eines Union-Typs
type UnionKeys<T> = T extends any ? keyof T : never;

type K = UnionKeys<Cat | Dog>;
// "meow" | "bark"

// OverloadedReturnType — letzten Overload auflösen
type OverloadedReturnType<T> =
  T extends {
    (...args: any[]): infer R;
    (...args: any[]): infer R;
    (...args: any[]): infer R;
    (...args: any[]): infer R;
  } ? R : never;

// Variance: Covariant vs Contravariant
type Covariant<T>     = () => T;     // T ist covariant (Ausgabe)
type Contravariant<T> = (x: T) => void;  // T ist contravariant (Eingabe)
Rekursions-Limit in TypeScript TypeScript limitiert Rekursionstiefe auf ca. 1000 Ebenen (konfigurierbar). Für sehr tiefe Strukturen empfiehlt sich ein iterativer Ansatz mit Tuple-Akkumulation oder die Nutzung von type-fest als Battle-tested Utility-Library.

TypeScript Advanced Types mit Claude Code meistern

Claude Code analysiert dein Typsystem, schlägt bessere Generic-Strukturen vor und hilft dir, Type-Fehler in Sekunden zu verstehen. Jetzt 14 Tage kostenlos testen.

Kostenlos starten →