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 →