Effect TS
Write idiomatic Effect code instead of promise-shaped TypeScript with Effect wrappers pasted on top.
Start
- Inspect the repo first:
package.json,tsconfig*, lockfile, existingeffect/@effect/*imports, and nearby tests. - Match the task to the smallest useful reference set below.
- If
effect-solutionsis installed, runeffect-solutions listandeffect-solutions show <topic>...before freehanding a pattern. - Follow local repo conventions before importing a new Effect pattern.
Read By Task
- Setup, install, tsconfig, or repo audit → references/setup-tooling.md
- Core application code → references/core-patterns.md
- HTTP, CLI, platform, or stream work → references/ecosystem-patterns.md
- Promise interop, framework boundaries, runtime issues, or gradual migration → references/adoption-runtime.md
Topic Map
effect-solutions show project-setup tsconfigfor bootstrap work.effect-solutions show basics services-and-layers data-modeling error-handling config testingfor core application code.effect-solutions show http-clients cli use-patternfor ecosystem and integration work.- Use
effect.website/docsfor deeper API detail, exhaustive module surfaces, and topics not yet covered here.
Defaults
- Use
Effect.genfor inline programs andEffect.fn("Name")for reusable named effectful functions. - Keep
Effect.run*at app edges, tests, workers, or framework adapters. - Put dependencies in services and layers, not hidden globals.
- Parse external data once at the boundary with
Schema, then pass typed values inward. - Model recoverable failures with tagged errors and narrow unions.
- Prefer test layers and
@effect/vitestover ad hoc mocks.
const loadUser = Effect.fn("loadUser")(function* (id: UserId) {
const repo = yield* UserRepo
return yield* repo.get(id)
})
const program = Effect.gen(function* () {
const input = yield* Schema.decodeUnknown(UserInput)(payload)
return yield* loadUser(input.id)
})Source Order
- Use local project code and tests as the primary contract.
- Use
effect-solutionsfor opinionated patterns and tradeoffs. - Use
effect.websitefor the canonical API surface. - If a local clone of the Effect repo exists, grep it for real implementations before guessing.
class UserRepo extends Context.Tag("UserRepo")<
UserRepo,
{ readonly get: (id: UserId) => Effect.Effect<User, UserNotFound> }
>() {}
const UserRepoLive = Layer.succeed(UserRepo, {
get: (id) => Effect.gen(function* () {
const sql = yield* SqlClient.SqlClient
const rows = yield* sql`SELECT * FROM users WHERE id = ${id}`
if (rows.length === 0) return yield* new UserNotFound({ id })
return rows[0] as User
}),
})Avoid
- Returning raw
Promisevalues from service methods unless the boundary forces it. - Calling
Effect.runPromisedeep inside domain code. - Using string errors when a tagged error should exist.
- Re-validating already parsed domain data in the core.
- Reaching for advanced abstractions before the simpler service / layer / schema model fits.