Claude Code & TypeScript: Typsicheres JavaScript meistern

JavaScript ist flexibel — manchmal zu flexibel. Eine Funktion, die string erwartet, erhält undefined. Ein API-Response hat plötzlich ein Feld mehr als erwartet. Ein Refactoring bricht zwanzig Stellen im Code, die man gar nicht auf dem Radar hatte. TypeScript löst genau diese Klasse von Problemen: nicht durch Einschränkung, sondern durch Explizitheit. Der Compiler weiß, was du weißt — und sagt dir, wenn beides auseinandergeht.

Mit Claude Code wird TypeScript noch wirkungsvoller. Nicht weil Claude Code TypeScript besser versteht als du — sondern weil es den gesamten Typgraphen deines Projekts gleichzeitig im Blick hat. Es sieht, wo ein Interface verwendet wird, wo ein Generic zu restriktiv ist, wo ein Type Guard fehlt. Dieser Artikel zeigt die wichtigsten TypeScript-Konzepte und wie Claude Code dabei konkret hilft.

Claude Code Mastery — TypeScript, Agents, Hooks auf Deutsch

Nicht nur TypeScript: der Kurs zeigt, wie du Claude Code wirklich produktiv einsetzt — für typsichere Entwicklung, autonome Agents und professionelle Workflows. Einmalig bezahlt, kein Abo.

Zum Kurs — Jetzt starten → Einmalzahlung · Kein Abo · 14 Tage Rückgaberecht

1. TypeScript vs. JavaScript: Was ändert sich wirklich?

TypeScript ist kein anderes JavaScript — es ist JavaScript mit einem optionalen Schicht darüber, die vor der Ausführung prüft, ob deine Annahmen über Typen korrekt sind. Der Browser führt am Ende trotzdem kompiliertes JavaScript aus. Der Unterschied liegt im Entwicklungsprozess: Fehler, die in JavaScript erst zur Laufzeit auftreten, schlägt TypeScript bereits beim Schreiben an.

Der häufigste Einwand: “TypeScript kostet Zeit.” Das stimmt in der Einführungsphase. Mittel- und langfristig spart typsicherer Code mehr Zeit als er kostet — weil Fehler früher gefunden werden und Refactorings sicherer sind.

2. tsconfig.json: Die richtigen Einstellungen von Anfang an

Die tsconfig.json steuert, wie strikt der TypeScript-Compiler prüft. Zu lax: TypeScript hilft kaum. Zu strikt für den Einstieg: der Compiler blockiert mehr als er unterstützt. Eine sinnvolle Ausgangskonfiguration für neue Projekte:

{
  "compilerOptions": {
    "target": "ES2022",
    "module": "ESNext",
    "moduleResolution": "bundler",
    "strict": true,
    "noUncheckedIndexedAccess": true,
    "exactOptionalPropertyTypes": true,
    "noImplicitReturns": true,
    "noFallthroughCasesInSwitch": true,
    "skipLibCheck": true,
    "outDir": "./dist",
    "rootDir": "./src"
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules", "dist"]
}

strict: true aktiviert mehrere Prüfungen gleichzeitig: strictNullChecks, strictFunctionTypes, noImplicitAny und andere. Das ist der wichtigste Schalter. noUncheckedIndexedAccess ergibt zusätzlich, dass Array-Zugriffe T | undefined zurückgeben — was erzwingt, dass man prüft, ob ein Element vorhanden ist.

Claude Code Tipp: Starte mit strict: true in einem neuen Projekt. Für die Migration eines bestehenden JS-Projekts lässt du strict zunächst weg und aktivierst die Optionen schrittweise — Claude Code kann dir eine priorisierte Reihenfolge empfehlen, die die meisten Fehler mit dem geringsten Aufwand beseitigt.

3. Basis-Typen und Interfaces

TypeScript kennt die primitiven JavaScript-Typen — string, number, boolean, null, undefined — und ergänzt sie um strukturierte Typen. Für Objekte gibt es zwei Ansätze: type und interface.

// Interface: erweiterbar, gut für Objekte und Klassen
interface User {
  id: number;
  name: string;
  email: string;
  role: 'admin' | 'user' | 'guest';
  createdAt: Date;
  lastLogin?: Date; // optional
}

// Type: flexibler, gut für Unions und Aliases
type ApiResponse<T> = {
  data: T;
  status: 'success' | 'error';
  message?: string;
};

// Verwendung
const fetchUser = async (id: number): Promise<ApiResponse<User>> => {
  const res = await fetch(`/api/users/${id}`);
  return res.json();
};

Die Faustregel: interface für Objekte, die andere erweitern können sollen, type für alles andere. In der Praxis sind beide oft austauschbar — Konsistenz im Projekt ist wichtiger als die Wahl selbst.

4. Generics: Typen die sich anpassen

Generics sind das mächtigste Feature von TypeScript. Sie erlauben es, Funktionen und Klassen zu schreiben, die mit verschiedenen Typen arbeiten — ohne dabei Typsicherheit aufzugeben.

// Ohne Generic: muss für jeden Typ neu geschrieben werden
function firstString(arr: string[]): string | undefined {
  return arr[0];
}

// Mit Generic: funktioniert für jeden Array-Typ
function first<T>(arr: T[]): T | undefined {
  return arr[0];
}

const name = first(['Alice', 'Bob']); // Typ: string | undefined
const num = first([1, 2, 3]);         // Typ: number | undefined

// Generic mit Constraint: T muss eine id-Property haben
function findById<T extends { id: number }>(
  items: T[],
  id: number
): T | undefined {
  return items.find(item => item.id === id);
}

Claude Code hilft besonders dabei, den richtigen Constraint für einen Generic zu finden. Häufiger Fehler: Generics zu restriktiv schreiben (T extends object statt des konkreten Feldes) oder zu weit (T extends any, was den Sinn aufhebt).

5. Type Guards: Zur Laufzeit Typen prüfen

TypeScript weiß zur Kompilierzeit, welche Typen möglich sind. Aber manche Informationen kommen erst zur Laufzeit — API-Responses, externe Daten, Union Types. Type Guards lösen dieses Problem: sie sind Funktionen, die TypeScript mitteilen, welcher konkrete Typ vorliegt.

interface Cat { kind: 'cat'; meows: boolean }
interface Dog { kind: 'dog'; barks: boolean }
type Animal = Cat | Dog;

// Discriminated Union: der einfachste Type Guard
function makeSound(animal: Animal): string {
  if (animal.kind === 'cat') {
    return animal.meows ? 'Miau' : '...'; // TypeScript kennt jetzt Cat
  }
  return animal.barks ? 'Wuff' : '...';   // TypeScript kennt jetzt Dog
}

// Custom Type Guard mit is-Syntax
function isUser(value: unknown): value is User {
  return (
    typeof value === 'object' &&
    value !== null &&
    'id' in value &&
    'name' in value &&
    typeof (value as User).id === 'number'
  );
}

// Verwendung: nach dem Guard kennt TypeScript den Typ
const data: unknown = await fetchSomething();
if (isUser(data)) {
  console.log(data.name); // TypeScript weiß: data ist User
}

Type Guards sind besonders wichtig bei API-Daten. Niemals as User verwenden, wenn die Daten von außen kommen — das ist ein Cast ohne Prüfung und bricht die Typsicherheit.

6. Enums und ihre Alternative

Enums gab es in TypeScript von Anfang an — aber die Community hat sie im Laufe der Zeit zunehmend durch const objects oder Union Types ersetzt, weil Enums in kompiliertem JavaScript unerwartet Laufzeit-Code erzeugen.

// Enum (klassisch, vermeiden in modernem TS)
enum Direction { Up, Down, Left, Right }

// Besser: const object mit as const
const Direction = {
  Up: 'UP',
  Down: 'DOWN',
  Left: 'LEFT',
  Right: 'RIGHT',
} as const;
type Direction = typeof Direction[keyof typeof Direction];
// Ergibt: type Direction = 'UP' | 'DOWN' | 'LEFT' | 'RIGHT'

// Noch simpler: direkter Union Type
type Status = 'pending' | 'active' | 'cancelled' | 'completed';

Empfehlung: Verwende String-Union-Types ('a' | 'b' | 'c') für einfache Fälle und as const-Objekte, wenn du die Werte an mehreren Stellen referenzieren musst. Klassische Enums sind nur noch in spezifischen Situationen sinnvoll, zum Beispiel bei Bit-Flags.

7. Utility Types: Partial, Required, Pick, Omit

TypeScript kommt mit eingebauten Utility Types, die häufige Typumwandlungen abkürzen. Sie sind in jedem ernsthaften Projekt unverzichtbar.

interface Product {
  id: number;
  name: string;
  price: number;
  description: string;
  inStock: boolean;
}

// Partial: alle Properties optional (z.B. für PATCH-Requests)
type ProductUpdate = Partial<Product>;

// Required: alle Properties verpflichtend (Gegenteil von Partial)
type CompleteProduct = Required<Product>;

// Pick: nur bestimmte Properties behalten
type ProductSummary = Pick<Product, 'id' | 'name' | 'price'>;

// Omit: bestimmte Properties entfernen
type ProductWithoutId = Omit<Product, 'id'>;

// Record: Objekt mit bekannten Keys und einheitlichem Value-Typ
type ProductMap = Record<number, Product>;

// Readonly: alle Properties schreibgeschützt
type ImmutableProduct = Readonly<Product>;

// ReturnType: Typ des Rückgabewerts einer Funktion extrahieren
type FetchResult = ReturnType<typeof fetchUser>;

Diese Utility Types reduzieren Duplikation erheblich. Statt zwei separate Interfaces für “neues Produkt” und “Produkt-Update” zu pflegen, leitet man sie aus dem Haupt-Interface ab. Änderungen müssen nur an einer Stelle gemacht werden.

8. Declaration Files: Typen für JavaScript-Bibliotheken

Nicht jede npm-Bibliothek ist in TypeScript geschrieben. Für viele Bibliotheken gibt es separate Type-Packages unter @types/ — zum Beispiel @types/node für Node.js-APIs oder @types/lodash.

# Typen für eine bestehende Bibliothek installieren
npm install -D @types/node @types/express

# Eigene Declaration File für eine Bibliothek ohne Typen
# src/types/untyped-lib.d.ts
declare module 'untyped-lib' {
  export function doSomething(input: string): Promise<void>;
  export interface Config {
    timeout: number;
    retries: number;
  }
}

Declaration Files (.d.ts) beschreiben die Typen einer Bibliothek, ohne Implementierungscode zu enthalten. Claude Code kann diese Dateien aus der Dokumentation oder dem JavaScript-Quellcode einer Bibliothek generieren — eine Aufgabe, die sonst mühsam ist.

9. Claude Code für die TypeScript-Migration

Der häufigste Anwendungsfall: ein bestehendes JavaScript-Projekt soll auf TypeScript migriert werden. Das klingt nach viel Arbeit — und war es auch, bevor es Claude Code gab. Der typische Ablauf:

claude "Dieses Projekt ist aktuell in JavaScript. Erstelle eine tsconfig.json
die zu unserem Setup passt (Node.js 22, ESM, keine Build-Tool-Konfiguration nötig)
und zeige mir, welche drei Dateien ich zuerst umbenennen und typisieren sollte,
um den größten Nutzen mit dem geringsten Aufwand zu haben."

Claude Code liest die Projektstruktur, erkennt, welche Dateien zentrale Datenstrukturen definieren oder die meisten Imports haben, und gibt eine priorisierte Reihenfolge. Dann:

claude "Konvertiere src/api/users.js nach TypeScript. Leite die Typen aus
den vorhandenen JSDoc-Kommentaren und den tatsächlichen Verwendungsstellen ab.
Verwende strikte Typen und verzichte auf 'any'."

Claude Code schreibt nicht einfach : any überall — es analysiert, welche Typen tatsächlich durch die Funktionen fließen, und schlägt konkrete Interfaces vor. Das spart nicht nur Zeit, sondern produziert bessere Typen als eine manuelle Schnellmigration.

Migrations-Strategie: Aktiviere "allowJs": true in der tsconfig, damit TypeScript- und JavaScript-Dateien nebeneinander existieren können. Migriere Datei für Datei, beginnend mit den zentralen Utilities und Datenmodellen. Aktiviere strict: true erst, wenn die Mehrheit der Dateien konvertiert ist.

Zwei weitere Artikel die auf diesem Thema aufbauen:


Claude Code Mastery — von TypeScript bis zum produktiven Agenten

TypeScript ist eine Stärke von Claude Code — aber nicht die einzige. Im Kurs lernst du Agents, MCP-Server, Hooks, Multi-Agent-Workflows und mehr. Vollständig auf Deutsch, einmalig bezahlt.

Jetzt starten → Einmalzahlung · Kein Abo · 14 Tage Rückgaberecht

Kurs · Claude Code Mastery

Von TypeScript zum produktiven AI-Agenten

TypeScript. Debugging. Agents. MCP. Hooks. Multi-Agent-Workflows. Alles auf Deutsch, einmalig bezahlt — kein Abo, keine Plattformabhängigkeit.

Jetzt einsteigen → Kursübersicht ansehen →

Einmalzahlung · Kein Abo · 14 Tage Rückgaberecht