Claude Code und TypeScript: schneller entwickeln, ohne Typsicherheit zu verlieren
Nutze strict, Zod, Unions, Generics, satisfies und Typ-Tests für bessere Claude-Code-Ergebnisse.
Claude Code kann TypeScript-Entwicklung stark beschleunigen, besonders bei Formularen, API-Helfern und Tests. Wenn Typgrenzen unklar sind, entstehen aber genauso schnell fragile Lösungen. Für Einsteiger ist die wichtigste Gewohnheit: erst die Typregeln festlegen, dann die Funktion generieren lassen.
strict bedeutet hier, dass TypeScript verdächtigen Code nicht leicht durchlässt.
Domänentypen schreiben Fachregeln als Typen auf.
Discriminated Unions modellieren Zustände mit unterschiedlichen Formen, und Laufzeitvalidierung prüft Daten während der Ausführung.
Claude Code zuerst eine Typkarte geben
Vor dem langen Prompt hilft eine kleine Karte: Compiler-Regeln, Domänentypen, externe Eingaben, Zustände und Typ-Tests. Damit wird der generierte Diff leichter überprüfbar.
flowchart TD
A["Anforderung"] --> B["tsconfig: strict Regeln"]
B --> C["Domänentypen: Plan und Account"]
C --> D["Externe Daten: unknown, dann validieren"]
D --> E["Zustand: Discriminated Unions"]
E --> F["Typ-Tests: expectTypeOf / tsd"]
F --> G["Implementierung und Review mit Claude Code"]
Offizielle Referenzen sind strict, noUncheckedIndexedAccess, exactOptionalPropertyTypes, Narrowing, Generics, Utility Types und die Notiz zu satisfies.
Für Laufzeitvalidierung ist die Zod-Dokumentation sinnvoll.
Passende interne Artikel: TypeScript Utility Types, TypeScript Generics und Zod Validation.
Mit strict tsconfig beginnen
Sage nicht nur “baue es in TypeScript”. Gib Claude Code zuerst den Compiler-Vertrag.
{
"compilerOptions": {
"target": "ES2022",
"module": "ESNext",
"moduleResolution": "Bundler",
"strict": true,
"noUncheckedIndexedAccess": true,
"exactOptionalPropertyTypes": true,
"noImplicitOverride": true,
"noFallthroughCasesInSwitch": true,
"skipLibCheck": true,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true
},
"include": ["src/**/*.ts", "src/**/*.tsx", "tests/**/*.ts"]
}
Der Prompt sollte die Grenzen nennen.
Dieses Repository nutzt strict TypeScript.
Führe kein any ein. Externe Eingaben als unknown annehmen und mit Zod validieren.
Bei switch über Unions eine never-Prüfung für Vollständigkeit ergänzen.
Nach der Umsetzung npx tsc --noEmit ausführen.
noUncheckedIndexedAccess lässt beim Lesen aus Arrays und Records undefined sichtbar.
Das ist strenger, findet aber fehlende API-Felder, leere Listen und unvollständige Übersetzungen früher.
Fall 1: SaaS-Pläne als Domänentypen
Domänentypen sind Fachregeln in TypeScript. Tarife, Rechte, Rechnungsstatus und Veröffentlichungsstatus gehören vor die UI.
export type Plan = "free" | "pro" | "enterprise";
export type Account = {
id: string;
email: string;
plan: Plan;
seats: number;
trialEndsAt: string | null;
};
export type CreateAccountInput = {
email: string;
plan: Exclude<Plan, "enterprise">;
seats?: number;
};
export type UpdateAccountInput = Partial<
Pick<Account, "email" | "plan" | "seats" | "trialEndsAt">
>;
Exclude entfernt Mitglieder aus einer Union.
Partial macht Eigenschaften optional. Das passt für Updates, ist aber gefährlich für Create-Inputs, wenn Pflichtfelder verschwinden.
Fall 2: API-Daten von unknown validieren
TypeScript-Typen existieren zur Laufzeit nicht.
APIs, Formulare, Cookies, localStorage, CSV und KI-Ausgaben können defekt sein.
Nimm sie als unknown an, validiere sie und nutze danach den typisierten Wert.
npm install zod
import { z } from "zod";
const AccountSchema = z.object({
id: z.string().min(1),
email: z.string().email(),
plan: z.enum(["free", "pro", "enterprise"]),
seats: z.number().int().positive(),
trialEndsAt: z.string().datetime().nullable()
});
type Account = z.infer<typeof AccountSchema>;
export function parseAccountResponse(json: unknown): Account {
return AccountSchema.parse(json);
}
unknown heißt: Der Wert ist noch nicht bewiesen.
Anders als any erzwingt es eine Prüfung vor dem Zugriff auf Eigenschaften.
Fall 3: Zahlungsstatus mit einer Union schließen
Zahlungen, Uploads, Formulare und Jobs sind Zustandsmaschinen.
status: string ist zu weit.
type PaymentResult =
| { status: "pending"; invoiceId: string }
| { status: "paid"; invoiceId: string; paidAt: string }
| { status: "failed"; invoiceId: string; reason: string };
export function renderPaymentMessage(result: PaymentResult): string {
switch (result.status) {
case "pending":
return `Invoice ${result.invoiceId} is waiting for payment.`;
case "paid":
return `Invoice ${result.invoiceId} was paid at ${result.paidAt}.`;
case "failed":
return `Invoice ${result.invoiceId} failed: ${result.reason}.`;
default: {
const exhaustive: never = result;
return exhaustive;
}
}
}
Der never-Zweig sorgt dafür, dass alle gültigen Fälle behandelt werden.
Kommt später refunded dazu, verlangt TypeScript den neuen Zweig.
Fall 4: Generics und satisfies
Generics machen Helfer wiederverwendbar, ohne den konkreten Typ am Aufrufort zu verlieren.
export function groupBy<T, K extends PropertyKey>(
items: readonly T[],
getKey: (item: T) => K
): Partial<Record<K, T[]>> {
const grouped: Partial<Record<K, T[]>> = {};
for (const item of items) {
const key = getKey(item);
const bucket = grouped[key] ?? [];
bucket.push(item);
grouped[key] = bucket;
}
return grouped;
}
const accounts = [
{ id: "a1", plan: "free" },
{ id: "a2", plan: "pro" },
{ id: "a3", plan: "pro" }
] as const;
const byPlan = groupBy(accounts, (account) => account.plan);
const proAccounts = byPlan.pro ?? [];
console.log(proAccounts.map((account) => account.id));
Für Konfigurationsobjekte ist satisfies meist besser als eine breite Assertion.
type ApiRoute = {
method: "GET" | "POST" | "PATCH" | "DELETE";
path: `/${string}`;
auth: boolean;
};
const routes = {
listAccounts: { method: "GET", path: "/accounts", auth: true },
createAccount: { method: "POST", path: "/accounts", auth: true },
healthCheck: { method: "GET", path: "/health", auth: false }
} as const satisfies Record<string, ApiRoute>;
type RouteName = keyof typeof routes;
export function getRoute(name: RouteName) {
return routes[name];
}
Typ-Tests ergänzen
Wichtige öffentliche Typen sollten getestet werden.
npm install -D vitest tsd
import { expectTypeOf, test } from "vitest";
type CreateAccountInput = {
email: string;
plan: "free" | "pro";
seats?: number;
};
test("CreateAccountInput keeps the public API narrow", () => {
expectTypeOf<CreateAccountInput>().toMatchTypeOf<{
email: string;
plan: "free" | "pro";
seats?: number;
}>();
});
Praxisfälle und Fehler (Use case / Pitfall checklist)
| Fall | Was das Typsystem festlegt | Was Claude Code erzeugen kann |
|---|---|---|
| SaaS-Abrechnung | Tarife, Rechnungsstatus, Rechte | UI-Zweige, Formulare, Texte |
| Admin-API | Zod-Schemas, Antworttypen | fetch, Tabellen, Ladezustände |
| Artikel-CMS | slug, Sprache, Publish-Status, Bild | MDX-Entwürfe, Listen, Validierung |
| Kontaktformular | Eingabeschema, Ergebnis-Union | UI, Submit-Handler, Tests |
| Fehler | Folge | Korrektur |
|---|---|---|
API-Antwort als any | Defektes JSON kompiliert | unknown und Zod |
status: string | Unmögliche Zustände | Discriminated Union |
Viele as User | Fehler werden versteckt | Schema, Guards, satisfies |
Partial<T> für Create | Pflichtfelder werden optional | create und update trennen |
| Keine Typ-Tests | Öffentliche Typen werden breiter | expectTypeOf oder tsd |
Bei ClaudeCodeLab erzeugte ein zu breites lang: string einmal ungültige Locale-URLs.
Eine geschlossene Union für die Sprachen machte Claude-Code-Änderungen deutlich verlässlicher.
Regeln und CTA
## TypeScript rules
- Use strict TypeScript.
- Do not introduce `any`. Use `unknown` at external boundaries.
- Prefer discriminated unions for states.
- Prefer `satisfies` over broad type assertions.
- Derive API types from Zod schemas when runtime data is involved.
- Add Vitest or tsd style type checks for exported helper types.
- Run `npx tsc --noEmit` before reporting completion.
Für Solo-Projekte bietet der Produktkatalog Templates und Checklisten für Claude Code.
Für Teams kann Claude Code Training und Beratung strict-Migration, Zod-Grenzen, Typ-Tests und CLAUDE.md-Regeln in einem echten Repository abdecken.
Geprüftes Ergebnis
Ich habe diesen Ablauf in einem kleinen TypeScript-Projekt getestet: API-Antworten von any auf unknown plus Zod umstellen, dann Claude Code Union-Zweige und expectTypeOf-Tests ergänzen lassen.
Der konkrete Nutzen war, fehlende Zustände und ungültige Property-Zugriffe vor dem Review zu finden.
Kostenloses PDF: Claude-Code-Cheatsheet
E-Mail eintragen und eine Seite mit Befehlen, Review-Gewohnheiten und sicheren Workflows herunterladen.
Wir schützen Ihre Daten und senden keinen Spam.
Über den Autor
Masa
Engineer für praktische Claude-Code-Workflows und Team-Einführung.
Ähnliche Artikel
Claude Code Permission Safety Ladder: Zugriff kontrolliert erweitern
Von read-only zu begrenzten Änderungen, Prüfbefehlen und Deploy-Checks mit klarer Kontrolle.
Claude Code Small PR Proof Pack: kleine Änderungen reviewbar machen
Ein Proof Pack für Claude-Code-PRs: Diff, Checks, öffentliche URL, CTA-Pfad und Rollback.
Claude-Code-Review-Gate vor dem Commit
Vor dem Commit mit Claude Code prüfen: Diff, Build, öffentliche URL, Gumroad-Links, Beratung-CTA, fehlende Tests und fremde Dateien.