| name | effectts |
| description | Idiomatic Effect-TS patterns for TypeScript functional programming. USE THIS SKILL WHEN: Writing new Effect services or layers, debugging Effect type errors (layer composition, error types), choosing between Effect patterns, setting up Effect testing. TRIGGERS ON: 'effect pattern', 'Layer.provide', 'Context.Tag', 'effect service', 'effect error', 'effect test', 'idiomatic effect'. NOT FOR: general TypeScript without Effect. |
| version | 1.0.0 |
Idiomatic Effect-TS
Write Effect code that looks like it came from the Effect core team.
Prerequisites (Optional)
Research First (ALWAYS)
1. Effect Docs MCP (preferred for concepts)
If you have effect-mcp configured:
mcp__effect - docs__effect_docs_search({ query: "Layer composition" });
mcp__effect - docs__get_effect_doc({ documentId: 123 });
Otherwise, check https://effect.website/docs for API reference.
2. Effect Source (preferred for real patterns)
rg "class .* extends Context\.Tag" <effect-repo>/packages/workflow/src
rg "class .* extends Context\.Tag" <effect-repo>/packages/cluster/src
rg "Effect\.Service" <effect-repo>/packages/cluster/src
rg "Schema\.TaggedError" <effect-repo>/packages/cluster/src
rg "Layer.provideMerge" <effect-repo>/packages/platform/src
Use parallel searches when patterns are unclear.
Quick Reference
Service Patterns (Choose Based on Use Case)
| Pattern | Use When | Layer Access |
|---|
Context.Tag | Library interfaces, multiple impls | ServiceName.Live |
Effect.Service | Concrete impls, tests, apps | ServiceName.Default |
export class MyService extends Context.Tag("app/MyService")<MyService, {
readonly doThing: (x: string) => Effect.Effect<Result, MyError>
}>() {
static readonly Live: Layer.Layer<MyService, never, Deps> = Layer.effect(...)
}
export class AppCache extends Effect.Service<AppCache>()("app/Cache", {
effect: Effect.gen(function* () {
return { get: (k) => ..., set: (k, v) => ... }
}),
dependencies: [SomeDep.Default]
}) {}
Domain Errors
export class MyError extends Schema.TaggedError<MyError>()("MyError", {
reason: Schema.Literal("NotFound", "Invalid"),
cause: Schema.optional(Schema.Defect),
}) {
static is(u: unknown): u is MyError {
return hasProperty(u, "_tag") && isTagged(u, "MyError");
}
}
Domain Types
export class Address extends Schema.Class<Address>("Address")({
host: Schema.String,
port: Schema.Number,
}) {}
export const UserId = Schema.NonEmptyString.pipe(
Schema.pattern(/^usr_[a-z0-9]+$/),
Schema.brand("UserId"),
);
export type UserId = typeof UserId.Type;
Layer Composition — COMMON GOTCHA
This causes most Effect type errors. Know the difference:
| Method | Deps Satisfied | Available to Program | Use When |
|---|
Layer.provide | Yes | No | Internal layer building |
Layer.provideMerge | Yes | Yes | Tests using multiple services |
Layer.mergeAll | No | Yes | Combining independent layers |
const TestLive = MyService.Live.pipe(Layer.provide(PlatformLive));
const TestLive = MyService.Live.pipe(Layer.provideMerge(PlatformLive));
Error pattern to recognize:
Effect<A, E, SomeService> is not assignable to Effect<A, E, never>
or diagnostic: Missing 'SomeService' in the expected Effect context
-> Means SomeService still required. Use provideMerge instead of provide.
Test Pattern
it.effect(
"works",
() =>
Effect.gen(function* () {
const svc = yield* MyService;
expect(yield* svc.doThing("x")).toBe(expected);
}).pipe(Effect.provide(TestLive)),
);
References (Read When Needed)
| Topic | File | When to Read |
|---|
| Service & Layer patterns | references/services.md | Creating services, Context.Tag vs Effect.Service, layer composition |
| Layer dependencies | references/layer-dependencies.md | How services access deps (yield + closure), why NOT factory functions |
| Schema decision matrix | references/schema-decision.md | Schema.Class vs Struct vs TaggedClass, branded types, migration patterns |
| Error handling | references/errors.md | Schema.TaggedError, TypeId, refail patterns |
| Domain types | references/domain-types.md | Schema.Class, branded types, TaggedRequest |
| Testing | references/testing.md | @effect/vitest setup, TDD workflow, test helpers |
| Collections & State | references/data.md | Chunk, Option, Ref patterns |
| Process management | references/processes.md | Scope, Command, background processes |
Anti-Patterns
| Anti-Pattern | Fix |
|---|
Data.TaggedError for domain errors | Use Schema.TaggedError |
Layer.mergeAll with dependent layers | Use Layer.provideMerge |
try/catch in Effect.gen | Use Effect.try or mapError |
Missing Effect.provide(...) in tests | Always provide at test boundary |
| Plain interfaces for domain types | Use Schema.Struct (or Schema.Class if needs behavior) |
| String IDs without branding | Use Schema.brand() |
| Guessing patterns | Grep Effect source first |
Effect.Service for library interfaces | Use Context.Tag |
| Factory function with service param | Yield deps in Effect.gen, close over (see layer-dependencies.md) |
Schema.Class for simple DTOs | Use Schema.Struct unless needs Equal/Hash |
| String literal union in struct | Use Schema.TaggedClass for variants |
Phantom type & { _tag } | Use Schema.brand() for runtime validation |
Bare Schema.String.pipe(Schema.brand(...)) | Add real constraints: NonEmptyString, pattern(), etc. |