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 →