TypeScript Generics mit Claude Code: Fortgeschrittene Typsicherheit 2026

TypeScript-Generics sind der Schlüssel zu wiederverwendbarem, typsicherem Code — Constraints, Conditional Types, Mapped Types, Template Literals. Claude Code kennt alle TypeScript-Patterns und generiert präzise Typ-Definitionen für jede Situation.

Generics gehören zu den mächtigsten Features von TypeScript — und gleichzeitig zu den am häufigsten falsch eingesetzten. In großen Projekten entstehen schnell unleserliche Typdefinitionen, die mehr Probleme schaffen als lösen. Claude Code hilft dabei, Generics präzise und idiomatisch einzusetzen: vom einfachen Generic-Parameter bis zu komplexen Conditional Types im API-Layer.

Dieser Artikel zeigt, wie du 2026 mit Claude Code Schicht für Schicht fortgeschrittene TypeScript-Typen aufbaust — mit echten Codebeispielen für React, Node.js und State-Management.

1. Generic Functions und Interfaces: Die Grundlage

Generics ermöglichen es, Funktionen und Interfaces so zu schreiben, dass sie mit mehreren Typen arbeiten, ohne die Typsicherheit aufzugeben. Claude Code generiert dabei immer den kleinstmöglichen, präzisesten Typ-Parameter.

Generic Wiederverwendbare API-Fetch-Funktion

Prompt an Claude Code: "Schreib eine generische fetch-Funktion, die den Response-Typ als Generic nimmt und Fehler typisiert zurückgibt."

// Claude Code Output: Generische Fetch-Funktion mit Fehlertypisierung interface ApiResponse<T> { data: T | null; error: string | null; status: number; } async function fetchApi<T>( url: string, options?: RequestInit ): Promise<ApiResponse<T>> { try { const res = await fetch(url, options); if (!res.ok) { return { data: null, error: `HTTP ${res.status}`, status: res.status }; } const data: T = await res.json(); return { data, error: null, status: res.status }; } catch (e) { return { data: null, error: (e as Error).message, status: 0 }; } } // Verwendung — vollständig typisiert interface User { id: number; name: string; email: string } const { data, error } = await fetchApi<User>('/api/users/1'); // data ist User | null — kein any, kein Cast!

Generic Generic Repository Pattern

// Claude Code: Generic Repository für beliebige Entitäten interface Entity { id: string | number } interface Repository<T extends Entity> { findById(id: T['id']): Promise<T | null>; findAll(): Promise<T[]>; save(entity: Omit<T, 'id'>): Promise<T>; update(id: T['id'], partial: Partial<T>): Promise<T | null>; delete(id: T['id']): Promise<boolean>; } // Konkrete Implementierung für User class UserRepository implements Repository<User> { async findById(id: number) { /* ... */ return null; } async findAll() { return []; } async save(data: Omit<User, 'id'>) { return { ...data, id: 1 }; } async update(id: number, partial: Partial<User>) { return null; } async delete(id: number) { return true; } }
Claude Code Tipp: Beschreibe das gewünschte Verhalten statt der Typen. Claude leitet die richtigen Generics aus dem Kontext ab — z.B. "Repository soll typsicher mit beliebigen DB-Entitäten funktionieren" führt automatisch zu korrekten Constraints.

2. Constraints mit extends, keyof und typeof

Constraints begrenzen, welche Typen als Generic-Parameter erlaubt sind. Claude Code setzt dabei extends, keyof und typeof präzise ein — ohne unnötige Einschränkungen.

Constraints keyof für typsichere Objektzugriffe

// Claude Code: Typsicherer Objekt-Getter mit keyof function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] { return obj[key]; } const user: User = { id: 1, name: 'Anna', email: 'anna@example.com' }; const name = getProperty(user, 'name'); // string ✓ const id = getProperty(user, 'id'); // number ✓ // getProperty(user, 'age') → Fehler: kein 'age' auf User ✓ // Erweiterung: Deep-Pick mit Pfad-Strings type DeepValue<T, P extends string> = P extends `${infer K}.${infer Rest}` ? K extends keyof T ? DeepValue<T[K], Rest> : never : P extends keyof T ? T[P] : never;

Constraints typeof für Laufzeit-Werte als Typen

// Claude Code: Config-Factory mit typeof-Constraint const defaultConfig = { apiUrl: 'https://api.example.com', timeout: 5000, retries: 3, debug: false, } as const; type Config = typeof defaultConfig; type ConfigKey = keyof Config; type ConfigValue<K extends ConfigKey> = Config[K]; function updateConfig<K extends ConfigKey>( key: K, value: ConfigValue<K> ): Config { return { ...defaultConfig, [key]: value }; } // updateConfig('timeout', 10000) → OK ✓ // updateConfig('debug', 'yes') → Fehler: 'yes' ist kein boolean ✓

3. Conditional Types: infer, Exclude, Extract, ReturnType

Conditional Types ermöglichen es, Typen basierend auf Bedingungen zu berechnen. Sie sind das Herzstück fortgeschrittener Typ-Utilities in modernen TypeScript-Projekten.

Conditional infer für Typ-Extraktion

Prompt: "Extrahiere den Promise-Wert-Typ und den Array-Element-Typ aus beliebigen Typen."

// Claude Code: Typ-Extraktion mit infer type Awaited<T> = T extends Promise<infer U> ? U : T; type ElementType<T> = T extends (infer E)[] ? E : never; type FunctionParams<T> = T extends (...args: infer P) => any ? P : never; // Anwendung type UserPromise = Promise<User>; type ResolvedUser = Awaited<UserPromise>; // User type UserList = User[]; type SingleUser = ElementType<UserList>; // User type FetchParams = FunctionParams<typeof fetchApi>; // [string, RequestInit?] // Tiefen-Unwrap für verschachtelte Promises type DeepAwaited<T> = T extends Promise<infer U> ? DeepAwaited<U> : T;

Conditional Exclude, Extract und ReturnType im API-Layer

// Claude Code: API-Typen mit Built-in Conditional Utilities type HttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE'; type SafeMethod = Extract<HttpMethod, 'GET'>; // 'GET' type MutationMethod = Exclude<HttpMethod, 'GET'>; // 'POST' | 'PUT' | 'PATCH' | 'DELETE' // ReturnType für automatische Response-Typisierung const apiHandlers = { getUser: async (id: number) => ({ id, name: 'Anna' }), listUsers: async () => [{ id: 1, name: 'Anna' }], createUser: async (data: { name: string }) => ({ id: 2, ...data }), }; type Handlers = typeof apiHandlers; type GetUserReturn = Awaited<ReturnType<Handlers['getUser']>>; // { id: number; name: string } — ohne manuell zu definieren! // NonNullable für sichere Nullability-Bereinigung type MaybeUser = User | null | undefined; type DefiniteUser = NonNullable<MaybeUser>; // User

4. Mapped Types: Partial, Required, Record, Pick, Omit

Mapped Types transformieren bestehende Typen systematisch. Claude Code nutzt sie präzise für DTO-Definitionen, State-Management und Validierungsschemas.

Mapped Fortgeschrittene Mapped Types für CRUD-DTOs

// Claude Code: DTO-Typen aus einer einzigen Entitätsdefinition interface Product { id: number; sku: string; name: string; price: number; stock: number; createdAt: Date; updatedAt: Date; } // Automatisch abgeleitete DTOs type CreateProductDto = Omit<Product, 'id' | 'createdAt' | 'updatedAt'>; type UpdateProductDto = Partial<Pick<Product, 'name' | 'price' | 'stock'>>; type ProductListItem = Pick<Product, 'id' | 'sku' | 'name' | 'price'>; // Record für typisierte Maps type ProductCatalog = Record<string, ProductListItem>; type StockMap = Record<Product['sku'], Product['stock']>; // Custom Mapped Type: alle Felder readonly machen type Immutable<T> = { readonly [K in keyof T]: T[K] }; type ImmutableProduct = Immutable<Product>; // alle Props readonly

Mapped Nullable und Validierungs-Mapped-Types

// Claude Code: Flexibler Validierungs-Typ für Formulare type FormErrors<T> = { [K in keyof T]?: string; }; type FormTouched<T> = { [K in keyof T]?: boolean; }; interface FormState<T> { values: T; errors: FormErrors<T>; touched: FormTouched<T>; isSubmitting: boolean; isValid: boolean; } // Verwendung mit konkretem Formtyp interface LoginForm { email: string; password: string; rememberMe: boolean } type LoginFormState = FormState<LoginForm>; // errors.email → string | undefined ✓ // touched.password → boolean | undefined ✓

5. Template Literal Types: Typen aus Strings berechnen

Template Literal Types kombinieren String-Literale zu neuen Typen. Claude Code setzt sie ein für Event-Namen, API-Routen und CSS-Klassen-Generierung.

Template Literal Event-System mit automatischen Typen

// Claude Code: Typsicheres Event-System für State-Management type EntityName = 'user' | 'product' | 'order'; type CrudAction = 'created' | 'updated' | 'deleted'; type DomainEvent = `${EntityName}:${CrudAction}`; // 'user:created' | 'user:updated' | 'user:deleted' // | 'product:created' | ... | 'order:deleted' (9 Typen) type EventHandler<E extends DomainEvent> = (event: E, payload: unknown) => void; class EventBus { private handlers = new Map<DomainEvent, EventHandler<any>[]>(); on<E extends DomainEvent>(event: E, handler: EventHandler<E>): void { const list = this.handlers.get(event) ?? []; this.handlers.set(event, [...list, handler]); } emit<E extends DomainEvent>(event: E, payload: unknown): void { this.handlers.get(event)?.forEach(h => h(event, payload)); } } const bus = new EventBus(); bus.on('user:created', (e, p) => console.log(e)); // OK ✓ // bus.on('admin:created', ...) → Fehler: kein gültiges Event ✓

Template Literal API-Routen-Typen

// Claude Code: Typisierte API-Routen mit Pfad-Parametern type ApiVersion = 'v1' | 'v2'; type Resource = 'users' | 'products' | 'orders'; type CollectionRoute = `/api/${ApiVersion}/${Resource}`; type ItemRoute = `/api/${ApiVersion}/${Resource}/${number}`; type ApiRoute = CollectionRoute | ItemRoute; // Getter/Setter mit automatischer Groß-/Kleinschreibung type Getters<T> = { [K in keyof T as `get${Capitalize<string & K>}`]: () => T[K] }; type Setters<T> = { [K in keyof T as `set${Capitalize<string & K>}`]: (v: T[K]) => void }; type UserAccessors = Getters<User> & Setters<User>; // { getId: () => number; setId: (v: number) => void; getName: ... }

6. Utility Types für API-Layer und State-Management

Im Produktionseinsatz kombiniert Claude Code mehrere Utility Types zu leistungsfähigen Abstraktionen — für fehlerfreie API-Integrationen und typsicheres State-Management in React.

Utility Vollständiger typsicherer API-Client

// Claude Code: Komplett typisierter API-Client für React/Next.js type ApiEndpoints = { 'GET /users': { response: User[] }; 'GET /users/:id': { params: { id: number }; response: User }; 'POST /users': { body: Omit<User, 'id'>; response: User }; 'PATCH /users/:id': { params: { id: number }; body: Partial<User>; response: User }; 'DELETE /users/:id': { params: { id: number }; response: { success: boolean } }; }; type EndpointKey = keyof ApiEndpoints; type EndpointConfig<K extends EndpointKey> = ApiEndpoints[K]; type ResponseType<K extends EndpointKey> = EndpointConfig<K>['response']; async function apiCall<K extends EndpointKey>( endpoint: K, config?: Omit<EndpointConfig<K>, 'response'> ): Promise<ResponseType<K>> { // Implementierung löst Pfad + Methode auf... throw new Error('not implemented'); } // Vollständig typisiert — IDE-Autovervollständigung funktioniert! const users = await apiCall('GET /users'); // User[] const user = await apiCall('GET /users/:id', { params: { id: 1 } }); // User

Utility State-Management mit Discriminated Unions

// Claude Code: Typsicherer State für async Data-Fetching type AsyncState<T, E = Error> = | { status: 'idle' } | { status: 'loading' } | { status: 'success'; data: T; lastFetched: Date } | { status: 'error'; error: E; retryCount: number }; function selectData<T>(state: AsyncState<T>): T | undefined { return state.status === 'success' ? state.data : undefined; } // Zustand-Transitionen typsicher type StateTransition<T> = { toLoading(): AsyncState<T>; toSuccess(data: T): AsyncState<T>; toError(error: Error, retryCount?: number): AsyncState<T>; reset(): AsyncState<T>; }; function createTransitions<T>(): StateTransition<T> { return { toLoading: () => ({ status: 'loading' }), toSuccess: (data) => ({ status: 'success', data, lastFetched: new Date() }), toError: (error, retryCount = 0) => ({ status: 'error', error, retryCount }), reset: () => ({ status: 'idle' }), }; }

Utility Zod-Integration für Runtime-Validierung

// Claude Code: TypeScript-Typen direkt aus Zod-Schemas ableiten import { z } from 'zod'; const UserSchema = z.object({ id: z.number().int().positive(), name: z.string().min(1).max(100), email: z.string().email(), role: z.enum(['admin', 'user', 'guest']), createdAt: z.date().optional(), }); // Typ direkt aus Schema — kein manuelles Interface nötig! type ValidatedUser = z.infer<typeof UserSchema>; // Generic Validator für beliebige Schemas function validate<S extends z.ZodType>(schema: S, data: unknown): z.infer<S> { return schema.parse(data); } // API-Handler mit automatischer Validierung function createHandler<T extends z.ZodType>( schema: T, handler: (data: z.infer<T>) => Promise<unknown> ) { return async (raw: unknown) => { const data = schema.parse(raw); return handler(data); }; }
Best Practice 2026: Kombiniere Zod-Schemas mit z.infer als Single Source of Truth — einmal das Schema definieren, Typen automatisch ableiten lassen. Claude Code generiert Schemas und zugehörige API-Handler in einem Schritt.

Claude Code als TypeScript-Pair-Programmer

Was Claude Code von einem Autocomplete-Tool unterscheidet: Es versteht den Zweck eines Typs und wählt das richtige Pattern. Für ein Formular-Validierungsproblem wählt es Mapped Types. Für ein Event-System empfiehlt es Template Literal Types. Für eine API-Integration kombiniert es Conditional Types und Utility Types.

Typische Prompts die präzise TypeScript-Typen erzeugen:

  • "Schreib einen typsicheren Event-Emitter für unsere Domain-Events user/product/order"
  • "Erstelle ein Generic Repository Interface das mit Supabase und Prisma funktioniert"
  • "Leite alle CRUD-DTOs automatisch aus unserem Prisma-Schema ab"
  • "Mach diesen API-Client vollständig typsicher ohne any"

Das Ergebnis: weniger Runtime-Fehler, bessere IDE-Unterstützung und Code der sich selbst dokumentiert — weil die Typen die Absicht präzise ausdrücken.

TypeScript-Modul im Kurs

Im Claude Code Mastery Kurs: vollständiges TypeScript-Modul mit Generics, Conditional Types, Mapped Types, Template Literals und fortgeschrittenen Utility Types — für maximale Typsicherheit.

14 Tage kostenlos testen →