atelier-typescript-effect-ts
npx skills add https://github.com/martinffx/claude-code-atelier --skill atelier-typescript-effect-ts
Agent 安装分布
Skill 文档
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
Successon success - May fail with an error of type
Error - Requires a context of type
Requirementsto run
import { Effect } from "effect"
// Effect<number, never, never> - succeeds with number, cannot fail, no requirements
const succeed = Effect.succeed(42)
// Effect<never, string, never> - always fails with string error
const fail = Effect.fail("Something went wrong")
// Effect<string, Error, never> - may succeed with string or fail with Error
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"
// From synchronous values
const fromValue = Effect.succeed(42)
const fromThunk = Effect.sync(() => Date.now())
// From failures
const fromError = Effect.fail(new Error("Failed"))
const fromDie = Effect.die("Unexpected error") // Defect, not typed
// From async operations
const fromPromise = Effect.tryPromise({
try: () => fetch("/api/data").then(r => r.json()),
catch: (e) => new Error(`Fetch failed: ${e}`)
})
// From nullable values
const fromNullable = Effect.fromNullable(maybeValue)
Running Effects
import { Effect } from "effect"
const program = Effect.succeed(42)
// Synchronous (throws if effect is async or fails)
const result = Effect.runSync(program) // 42
// Promise-based
const promise = Effect.runPromise(program) // Promise<42>
// With exit status
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 }
})
// Equivalent to:
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")
}
}
// Effect<User, UserNotFound | DatabaseError, never>
const getUser = (id: string) => Effect.gen(function* () {
const record = yield* queryDatabase(id)
if (!record) {
return yield* Effect.fail(new UserNotFound(id))
}
return record
})
// Handle specific errors
const handled = getUser("123").pipe(
Effect.catchTag("UserNotFound", (e) =>
Effect.succeed({ id: e.userId, name: "Anonymous" })
)
)
// Handle all errors
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"
// Define a service interface and tag
class Database extends Context.Tag("Database")<
Database,
{
readonly query: (sql: string) => Effect.Effect<unknown[]>
readonly execute: (sql: string) => Effect.Effect<void>
}
>() {}
// Use the service in effects
const getUsers = Effect.gen(function* () {
const db = yield* Database
return yield* db.query("SELECT * FROM users")
})
// Create a layer that provides the service
const DatabaseLive = Layer.succeed(Database, {
query: (sql) => Effect.sync(() => {
console.log(`Executing: ${sql}`)
return []
}),
execute: (sql) => Effect.sync(() => {
console.log(`Executing: ${sql}`)
})
})
// Provide the layer to run the effect
const runnable = Effect.provide(getUsers, DatabaseLive)
Effect.runPromise(runnable)
Schema Validation
Use Effect Schema for type-safe data validation:
import { Schema } from "effect"
// Define a schema
const User = Schema.Struct({
id: Schema.String,
name: Schema.String,
email: Schema.String,
age: Schema.Number
})
// Infer types from schema
type User = Schema.Schema.Type<typeof User>
// Decode unknown data
const decoded = Schema.decodeUnknownSync(User)({
id: "123",
name: "Alice",
email: "alice@example.com",
age: 30
})
// Decode with Effect (for better error handling)
const decodeEffect = Schema.decodeUnknown(User)
const result = decodeEffect({ id: "123", name: "Alice", email: "a@b.com", age: 30 })
// Effect<User, ParseError, never>
Resource Management
Safely manage resources that need cleanup:
import { Effect, Scope } from "effect"
// Define a resource with acquisition and release
const withConnection = Effect.acquireRelease(
// Acquire
Effect.sync(() => {
console.log("Opening connection")
return { query: (sql: string) => sql }
}),
// Release
(conn) => Effect.sync(() => {
console.log("Closing connection")
})
)
// Use the resource in a scoped effect
const program = Effect.scoped(
Effect.gen(function* () {
const conn = yield* withConnection
return conn.query("SELECT * FROM users")
})
)
// Connection is automatically closed after use
Effect.runPromise(program)
Common Patterns
Sequential vs Parallel Execution
import { Effect } from "effect"
const tasks = [task1, task2, task3]
// Sequential execution
const sequential = Effect.all(tasks, { concurrency: 1 })
// Parallel execution (all at once)
const parallel = Effect.all(tasks, { concurrency: "unbounded" })
// Parallel with limit
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.genfor complex flows – Generators make sequential operations readable - Tag errors with
_tag– EnablescatchTagfor precise error handling - Define services with
Context.Tag– Makes dependencies explicit and testable - Use
Layerfor service composition – Layers compose and manage lifecycle - Prefer
Effect.tryover 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/runSyncat application boundaries
Integration with Other Skills
Effect-TS integrates well with:
- Functional Patterns – Effect builds on Option/Either concepts
- Drizzle ORM – Wrap Drizzle queries in Effect for typed errors
- Fastify – Use Effect for request handling with typed errors
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