Guides working with Effect-TS in TypeScript codebases. Use when writing Effect programs, defining services/layers, handling errors, running effects, or when code uses effect, Context, Layer, Effect.gen, or related Effect patterns.
Effect is a TypeScript library for building complex synchronous and asynchronous programs with typed errors, dependency injection, and resource management. Reference: https://effect.website/llms.txt.
Effect<Success, Error, Requirements> is a lazy, immutable description of a workflow:
never when effect cannot fail.Context; use never when none needed.Effects are descriptions, not executions. They run via the Effect runtime at a single entry point.
import { Effect, Context, Layer } from 'effect';
// Effect<number, never, never> - succeeds with number, no errors, no deps
const pure = Effect.succeed(42);
// Effect<never, Error, never> - fails with Error
const failure = Effect.fail(new Error('failed'));
| Type | Tracked in type | Purpose |
|---|---|---|
| Expected | Yes (Error in Effect) | Anticipated, domain errors, recoverable (like checked exceptions) |
| Unexpected | No | Defects, bugs, unanticipated (like unchecked exceptions) |
Use Effect.fail() for expected errors. Thrown errors in sync/async code become defects. Avoid throw; prefer Effect.fail or Effect.try.
| Constructor | Use case |
|---|---|
Effect.succeed(v) | Pure success |
Effect.fail(e) | Expected failure |
Effect.sync(() => x) | Sync side effect; must not throw |
Effect.try(() => x) | Sync that may throw → defect or caught |
Effect.promise(() => Promise) | Wrap Promise (reject → defect) |
Effect.gen(function* () { ... }) | Generator style, like async/await |
const program = Effect.gen(function* () {
const a = yield* Effect.succeed(1);
const b = yield* Effect.succeed(2);
return a + b;
});
| Function | Returns | When to use |
|---|---|---|
Effect.runSync(e) | A | Sync only, no fail; throws otherwise |
Effect.runPromise(e) | Promise<A> | Async; rejects on failure |
Effect.runPromiseExit(e) | Promise<Exit<A, E>> | Get Exit for custom handling |
Provide requirements before running: Effect.runPromise(Effect.provide(program, layer)).
Context.Tag:class MyService extends Context.Tag('MyService')<
MyService,
{ readonly doSomething: () => Effect.Effect<string, never> }
>() {}
Requirements:const program = Effect.gen(function* () {
const service = yield* MyService;
return yield* service.doSomething();
});
// Effect<string, never, MyService>
Effect.provideService or Effect.provide + Layer.Layers construct services and hide implementation dependencies. Avoid leaking internal deps in service interfaces.
Layer.succeed(Tag, implementation) – static implementationLayer.effect(Tag, Effect) – effectful constructionLayer.merge(a, b) – combine layersLayer.provide / Layer.provideMerge for dependency graphsProvide at the edge: Effect.provide(program, AppLive).
Effect.gen over manual flatMap chains.Effect.catchAll, Effect.catchTag, Effect.orElse, Effect.retry.Effect.scoped, Scope, acquireRelease.Effect.all (parallel), Effect.forEach with concurrency.Data.TaggedClass or Data.TaggedEnum for typed domain errors.Import:
import { Effect, Context, Layer, Data } from 'effect';
Run program with layer:
const runnable = Effect.provide(program, AppLive);
Effect.runPromise(runnable);
Typed error:
class HttpError extends Data.TaggedClass('HttpError')<{ readonly status: number }> {}
For deeper topics, see https://effect.website/docs/: