| name | typescript-effect-ts |
| description | Type-safe functional effects with Effect-TS. Use when building applications with Effect, using Effect.gen generators, handling typed errors, managing services with Layer and Context.Tag, validating data with Schema, or managing resources with acquireRelease. |
| user-invocable | false |
Effect-TS
Effect is a powerful TypeScript library for building robust, type-safe applications. It provides a functional effect system with typed errors, dependency injection, resource management, and concurrency primitives.
Why Effect?
Type-safe errors: Unlike Promise which loses error type information, Effect tracks errors in the type system with Effect<Success, Error, Requirements>.
Dependency injection: Services are declared explicitly using Context.Tag and provided via Layer, making dependencies visible in types.
Resource safety: Effect.acquireRelease ensures resources are always cleaned up, even on failure or interruption.
Composability: Effects compose naturally with pipe, Effect.gen generators, and combinators like Effect.all, Effect.flatMap.
Quick Reference
For detailed patterns and examples, see:
- Core Concepts - Effect type, creating and running effects, generators
- Error Handling - Typed errors, catchAll, catchTag, Either
- Services & Layers - Dependency injection with Context.Tag and Layer
- Schema - Data validation with encode/decode
- Resources - Resource management with acquireRelease and Scope
The Effect Type
The core type Effect<Success, Error, Requirements> represents a computation that:
- Produces a value of type
Success on success
- May fail with an error of type
Error
- Requires a context of type
Requirements to run
import { Effect } from "effect"
const succeed = Effect.succeed(42)
const fail = Effect.fail("Something went wrong")
const parse = (input: string): Effect.Effect<number, Error> =>
Effect.try({
try: () => JSON.parse(input),
catch: (e) => new Error(`Parse failed: ${e}`)
})
Creating Effects
import { Effect } from "effect"
const fromValue = Effect.succeed(42)
const fromThunk = Effect.sync(() => Date.now())
const fromError = Effect.fail(new Error("Failed"))
const fromDie = Effect.die("Unexpected error")
const fromPromise = Effect.tryPromise({
try: () => fetch("/api/data").then(r => r.json()),
catch: (e) => new Error(`Fetch failed: ${e}`)
})
const fromNullable = Effect.fromNullable(maybeValue)
Running Effects
import { Effect } from "effect"
const program = Effect.succeed(42)
const result = Effect.runSync(program)
const promise = Effect.runPromise(program)
const exit = Effect.runSyncExit(program)
const exitPromise = Effect.runPromiseExit(program)
Effect.gen Generators
Write async-looking code with full type safety using generators:
import { Effect } from "effect"
const program = Effect.gen(function* () {
const user = yield* fetchUser(userId)
const posts = yield* fetchPosts(user.id)
const enriched = yield* enrichPosts(posts)
return { user, posts: enriched }
})
const programPipe = fetchUser(userId).pipe(
Effect.flatMap(user =>
fetchPosts(user.id).pipe(
Effect.flatMap(posts =>
enrichPosts(posts).pipe(
Effect.map(enriched => ({ user, posts: enriched }))
)
)
)
)
)
Error Handling
Effect tracks errors in the type system, making error handling explicit:
import { Effect } from "effect"
class UserNotFound extends Error {
readonly _tag = "UserNotFound"
constructor(readonly userId: string) {
super(`User not found: ${userId}`)
}
}
class DatabaseError extends Error {
readonly _tag = "DatabaseError"
constructor(readonly cause: unknown) {
super("Database error")
}
}
const getUser = (id: string) => Effect.gen(function* () {
const record = yield* queryDatabase(id)
if (!record) {
return yield* Effect.fail(new UserNotFound(id))
}
return record
})
const handled = getUser("123").pipe(
Effect.catchTag("UserNotFound", (e) =>
Effect.succeed({ id: e.userId, name: "Anonymous" })
)
)
const recovered = getUser("123").pipe(
Effect.catchAll((error) =>
Effect.succeed({ id: "unknown", name: "Fallback" })
)
)
Services and Dependency Injection
Define services with Context.Tag and provide them with Layer:
import { Context, Effect, Layer } from "effect"
class Database extends Context.Tag("Database")<
Database,
{
readonly query: (sql: string) => Effect.Effect<unknown[]>
readonly execute: (sql: string) => Effect.Effect<void>
}
>() {}
const getUsers = Effect.gen(function* () {
const db = yield* Database
return yield* db.query("SELECT * FROM users")
})
const DatabaseLive = Layer.succeed(Database, {
query: (sql) => Effect.sync(() => {
console.log(`Executing: ${sql}`)
return []
}),
execute: (sql) => Effect.sync(() => {
console.log(`Executing: ${sql}`)
})
})
const runnable = Effect.provide(getUsers, DatabaseLive)
Effect.runPromise(runnable)
Schema Validation
Use Effect Schema for type-safe data validation:
import { Schema } from "effect"
const User = Schema.Struct({
id: Schema.String,
name: Schema.String,
email: Schema.String,
age: Schema.Number
})
type User = Schema.Schema.Type<typeof User>
const decoded = Schema.decodeUnknownSync(User)({
id: "123",
name: "Alice",
email: "alice@example.com",
age: 30
})
const decodeEffect = Schema.decodeUnknown(User)
const result = decodeEffect({ id: "123", name: "Alice", email: "a@b.com", age: 30 })
Resource Management
Safely manage resources that need cleanup:
import { Effect, Scope } from "effect"
const withConnection = Effect.acquireRelease(
Effect.sync(() => {
console.log("Opening connection")
return { query: (sql: string) => sql }
}),
(conn) => Effect.sync(() => {
console.log("Closing connection")
})
)
const program = Effect.scoped(
Effect.gen(function* () {
const conn = yield* withConnection
return conn.query("SELECT * FROM users")
})
)
Effect.runPromise(program)
Common Patterns
Sequential vs Parallel Execution
import { Effect } from "effect"
const tasks = [task1, task2, task3]
const sequential = Effect.all(tasks, { concurrency: 1 })
const parallel = Effect.all(tasks, { concurrency: "unbounded" })
const limited = Effect.all(tasks, { concurrency: 5 })
Retry with Backoff
import { Effect, Schedule } from "effect"
const retried = fetchData.pipe(
Effect.retry(
Schedule.exponential("100 millis").pipe(
Schedule.jittered,
Schedule.compose(Schedule.recurs(5))
)
)
)
Timeouts
import { Effect, Duration } from "effect"
const withTimeout = longRunningTask.pipe(
Effect.timeout(Duration.seconds(30))
)
Guidelines
- Use
Effect.gen for complex flows - Generators make sequential operations readable
- Tag errors with
_tag - Enables catchTag for precise error handling
- Define services with
Context.Tag - Makes dependencies explicit and testable
- Use
Layer for service composition - Layers compose and manage lifecycle
- Prefer
Effect.try over manual try/catch - Keeps errors in the Effect channel
- Use Schema for external data - Validates and transforms API/DB data
- Scope resources with
acquireRelease - Guarantees cleanup on success or failure
- Run effects at the edge - Keep
runPromise/runSync at application boundaries
Integration with Other Skills
Effect-TS integrates well with:
When This Skill Loads
This skill automatically loads when discussing:
- Effect-TS library usage
- Typed error handling in TypeScript
- Dependency injection with Context and Layer
- Effect.gen generators
- Schema validation with Effect
- Resource management and Scope
- Functional effect systems