effect-ts

Installation
SKILL.md

Effect TypeScript Best Practices

Effect is a TypeScript library for building complex, type-safe applications with structured error handling, dependency injection via services/layers, fiber-based concurrency, and resource safety.

When to Apply

  • Writing or reviewing TypeScript code that imports from effect, @effect/schema, or @effect/platform
  • Implementing typed error handling with Effect<Success, Error, Requirements>
  • Building services and layers for dependency injection
  • Working with Schema for data validation, decoding, and transformation
  • Using fiber-based concurrency (queues, semaphores, PubSub, deferred)
  • Processing data with Stream and Sink
  • Migrating from Promises, fp-ts, neverthrow, or ZIO to Effect

How to Use

This skill is organized by domain. Read the relevant reference file for the area you're working in.

Read First: The Paradigm

Always read this before diving into API references, especially when refactoring existing code to use Effect or writing new Effect services:

Reference When to Read
Think in Effect: The Paradigm Shift Before any other reference. Mental model shifts, refactoring recipes, anti-patterns, application architecture. Read this to understand HOW to think in Effect — the other files teach WHAT to type.

Core Foundations

Reference When to Read
Getting Started Creating the Effect type, pipelines, generators, running effects
Error Management Typed errors, recovery, retrying, timeouts, sandboxing
Core Concepts Request batching, configuration management, runtime system

Data & Validation

Reference When to Read
Data Types Option, Either, Cause, Chunk, DateTime, Duration, Exit, Data
Schema Basics Schema intro, basic usage, classes, constructors, effect data types
Schema Advanced Transformations, filters, annotations, error formatting, JSON Schema output

Architecture & Dependencies

Reference When to Read
Requirements Management Services, Layers, dependency injection, layer memoization
Resource Management Scope, safe resource acquisition/release, caching
State Management Ref, SubscriptionRef, SynchronizedRef for concurrent state

Concurrency & Streaming

Reference When to Read
Concurrency Fibers, Deferred, Latch, PubSub, Queue, Semaphore
Streams and Sinks Creating, consuming, transforming streams; sink operations
Scheduling Built-in schedules, cron, combinators, repetition

Platform & Observability

Reference When to Read
Platform FileSystem, Command, Terminal, KeyValueStore, Path
Observability Logging, metrics, tracing, Supervisor
Testing TestClock for time simulation; for service mocking and layer testing, see Requirements Management

Style, AI & Migration

Reference When to Read
Code Style Branded types, pattern matching, dual APIs, guidelines, traits
AI Integration Effect AI packages for LLM tool use and execution planning
Micro Lightweight Effect alternative for smaller bundles
Migration Guides Coming from Promises, fp-ts, neverthrow, or ZIO

Quick Reference — Common Patterns

The Effect Type

//         ┌─── Success type
//         │        ┌─── Error type
//         │        │      ┌─── Required dependencies
//         ▼        ▼      ▼
Effect<Success, Error, Requirements>

Creating Effects

import { Effect } from "effect"

// From sync values
const succeed = Effect.succeed(42)
const fail = Effect.fail(new Error("oops"))

// From sync code that may throw
const sync = Effect.try(() => JSON.parse(data))

// From promises
const async = Effect.tryPromise(() => fetch(url))

// From generators (recommended for complex flows)
const program = Effect.gen(function* () {
  const user = yield* getUser(id)
  const todos = yield* getTodos(user.id)
  return { user, todos }
})

Running Effects

// Async (returns Promise)
Effect.runPromise(program)

// With full Exit information
Effect.runPromiseExit(program)

// Sync (throws on async)
Effect.runSync(program)

Typed Errors

import { Data, Effect } from "effect"

class NotFound extends Data.TaggedError("NotFound")<{
  readonly id: string
}> {}

class Unauthorized extends Data.TaggedError("Unauthorized")<{}> {}

// Error type is tracked: Effect<User, NotFound | Unauthorized>
const getUser = (id: string) =>
  Effect.gen(function* () {
    // ...
  })

Services and Layers

import { Context, Effect, Layer } from "effect"

// Define a service
class UserRepo extends Context.Tag("UserRepo")<
  UserRepo,
  { readonly findById: (id: string) => Effect.Effect<User, NotFound> }
>() {}

// Use in effects — adds to Requirements channel
const program = Effect.gen(function* () {
  const repo = yield* UserRepo
  return yield* repo.findById("1")
})

// Implement with a Layer
const UserRepoLive = Layer.succeed(UserRepo, {
  findById: (id) => Effect.succeed({ id, name: "Alice" })
})

// Provide and run
program.pipe(Effect.provide(UserRepoLive), Effect.runPromise)

Schema Validation

import { Schema } from "effect"

const User = Schema.Struct({
  id: Schema.Number,
  name: Schema.String,
  email: Schema.String.pipe(Schema.pattern(/@/))
})

type User = typeof User.Type

// Decode (parse + validate)
const decode = Schema.decodeUnknownSync(User)
const user = decode({ id: 1, name: "Alice", email: "a@b.com" })

Pipelines

import { Effect, pipe } from "effect"

// Data-last (pipe style)
const result = pipe(
  getTodos,
  Effect.map((todos) => todos.filter((t) => !t.done)),
  Effect.flatMap((active) => sendNotification(active.length)),
  Effect.catchTag("NetworkError", () => Effect.succeed("offline"))
)

// Fluent (method style)
const result2 = getTodos.pipe(
  Effect.map((todos) => todos.filter((t) => !t.done)),
  Effect.flatMap((active) => sendNotification(active.length))
)

Gotchas

See gotchas.md for known failure points.

Weekly Installs
80
GitHub Stars
131
First Seen
1 day ago