Effect-TS mit Claude Code: Typsichere Fehlerbehandlung 2026

Effect-TS bringt Haskell-inspirierte Robustheit nach TypeScript — vollständige Typsicherheit für Fehler, Abhängigkeiten und Nebeneffekte. Claude Code versteht alle Effect-Patterns und generiert produktionsreife Implementierungen.

Der Effect-Typ: Fehler als First-Class Citizens

CoreEffect<Success, Error, Requirements>

# Prompt: "Refaktoriere diese async-Funktion zu Effect-TS mit typsicheren Fehlern" import { Effect, pipe } from "effect"; // Typisierte Fehler — kein undifferenziertes `Error` mehr class DatabaseError { readonly _tag = "DatabaseError"; constructor(readonly message: string) {} } class NotFoundError { readonly _tag = "NotFoundError"; constructor(readonly id: string) {} } // Effect<User, DatabaseError | NotFoundError, never> const getUser = (id: string) => Effect.tryPromise({ try: () => db.user.findUnique({ where: { id } }), catch: (e) => new DatabaseError(String(e)), }).pipe( Effect.flatMap((user) => user ? Effect.succeed(user) : Effect.fail(new NotFoundError(id)) ) ); // Fehler sicher behandeln — TypeScript kennt beide Fehlertypen! const program = getUser("user-123").pipe( Effect.catchTag("NotFoundError", (e) => Effect.succeed({ id: e.id, name: "Anonymous" }) ), Effect.catchTag("DatabaseError", (e) => Effect.fail(new Error(`DB-Fehler: ${e.message}`)) ) ); // Ausführen: Effect.runPromise(program).then(console.log);
Effect-Prompt-Tipp: "Erstelle typsichere Fehlerklassen für [Domain] und refaktoriere die async-Funktionen zu Effect-TS. Alle Fehler sollen im Typ sichtbar sein." Claude Code erzeugt vollständig typisierte Effect-Programme.

Services und Dependency Injection

ServicesContext und Layer-System

# Prompt: "UserService mit Effect-TS Context, testbar durch Layer-Substitution" import { Context, Layer, Effect } from "effect"; // Service-Interface definieren class UserService extends Context.Tag("UserService")< UserService, { getUser: (id: string) => Effect.Effect<User, NotFoundError>; createUser: (data: CreateUserDto) => Effect.Effect<User, DatabaseError>; } >() {} // Produktive Implementierung const UserServiceLive = Layer.succeed(UserService, { getUser: (id) => Effect.tryPromise({ try: () => db.user.findUnique({ where: { id } }), catch: (e) => new NotFoundError(id), }), createUser: (data) => Effect.tryPromise({ try: () => db.user.create({ data }), catch: (e) => new DatabaseError(String(e)), }), }); // Test-Implementierung (kein echtes DB nötig!) const UserServiceTest = Layer.succeed(UserService, { getUser: (id) => Effect.succeed({ id, name: "Test User", email: "test@test.com" }), createUser: (data) => Effect.succeed({ id: "test-id", ...data }), }); // Programm schreiben (agnostisch zur Implementierung) const program = Effect.gen(function* () { const service = yield* UserService; // Dependency automatisch injiziert const user = yield* service.getUser("123"); return user; }); // Prod vs Test — nur Layer tauschen: Effect.runPromise(Effect.provide(program, UserServiceLive)); Effect.runPromise(Effect.provide(program, UserServiceTest)); // Tests!

Concurrency: Fiber-basierte Parallelität

ConcurrencyParallelität ohne Race-Conditions

# Prompt: "3 API-Calls parallel mit Timeout und automatischem Retry" import { Effect, Schedule } from "effect"; // Parallel ausführen — alle müssen erfolgreich sein const [user, orders, recommendations] = yield* Effect.all( [fetchUser(userId), fetchOrders(userId), fetchRecommendations(userId)], { concurrency: "unbounded" } // Alle parallel ); // Mit Timeout pro Request const withTimeout = fetchUser(userId).pipe( Effect.timeout("3 seconds"), Effect.catchTag("TimeoutException", () => Effect.succeed(null) // Timeout = null statt Fehler ) ); // Exponential Retry mit Jitter const withRetry = fetchUser(userId).pipe( Effect.retry( Schedule.exponential("100 millis").pipe( Schedule.jittered, Schedule.upTo("30 seconds") ) ) ); // Race — wer zuerst antwortet gewinnt const fastest = Effect.race( fetchFromPrimaryDB(id), fetchFromReplicaDB(id) );
Fiber-Ressourcen: Effect verwaltet Fibers automatisch — bei Cancellation werden alle Sub-Fibers aufgeräumt. Kein Memory-Leak-Risiko wie mit rohen Promises. Claude Code erklärt die Fiber-Semantik und generiert korrekten Interrupt-sicheren Code.

Schema: Typsichere Validation

# Effect/Schema — mächtiger als Zod, Teil des Effect-Ökosystems import { Schema } from "effect"; const UserSchema = Schema.Struct({ id: Schema.String, email: Schema.String.pipe(Schema.pattern(/.+@.+/)), age: Schema.Number.pipe(Schema.between(0, 150)), role: Schema.Literal("admin", "user"), createdAt: Schema.Date, }); type User = Schema.Schema.Type<typeof UserSchema>; // Decoding mit typsicheren Fehlern: const decoded = Schema.decodeUnknown(UserSchema)(rawInput).pipe( Effect.mapError((e) => new ValidationError(e.message)) ); // Encoding (User → JSON-kompatibles Objekt): const encoded = Schema.encode(UserSchema)(user);

Funktionale TypeScript im Kurs

Im Claude Code Mastery Kurs: vollständiges Effect-TS-Modul mit Effect-Typ, Services, Layers, Concurrency und Schema — inkl. Migration von async/await und Zod zu Effect-TS für produktionsreife Anwendungen.

14 Tage kostenlos testen →