TypeScript has no standard library for errors, retries, concurrency, or scheduling. Effect fills that gap — typed errors, resource management, dependency injection, and more.
What Is Effect?
Effect is a comprehensive TypeScript library that provides tools for managing errors, async operations, concurrency, and application architecture. Think of it as "what TypeScript's standard library should be."
Quick Start
npm install effect
import { Effect, Console } from 'effect';
const program = Effect.gen(function* () {
yield* Console.log('Hello from Effect!');
const result = yield* Effect.succeed(42);
yield* Console.log(`Result: ${result}`);
return result;
});
Effect.runPromise(program);
Typed Errors (The Killer Feature)
class UserNotFoundError {
readonly _tag = 'UserNotFoundError';
constructor(readonly userId: string) {}
}
class DatabaseError {
readonly _tag = 'DatabaseError';
constructor(readonly message: string) {}
}
// TypeScript KNOWS which errors this can throw
const getUser = (id: string): Effect.Effect<User, UserNotFoundError | DatabaseError> =>
Effect.gen(function* () {
const db = yield* connectToDb(); // Can fail with DatabaseError
const user = yield* findUser(db, id); // Can fail with UserNotFoundError
return user;
});
// Handle specific errors
const program = getUser('123').pipe(
Effect.catchTag('UserNotFoundError', (err) =>
Effect.succeed({ id: err.userId, name: 'Unknown' })
),
// DatabaseError still propagates — TypeScript enforces this!
);
Retries
const fetchData = pipe(
Effect.tryPromise(() => fetch('/api/data')),
Effect.retry({
times: 3,
schedule: Schedule.exponential('1 second'),
}),
);
Concurrency
// Run 3 tasks in parallel, max 5 concurrent
const results = yield* Effect.forEach(
urls,
(url) => fetchUrl(url),
{ concurrency: 5 }
);
// Race — first to complete wins
const fastest = yield* Effect.race([
fetchFromServer1(query),
fetchFromServer2(query),
]);
// All must succeed
const [users, posts, comments] = yield* Effect.all([
getUsers(),
getPosts(),
getComments(),
]);
Resource Management
const dbPool = Effect.acquireRelease(
// Acquire
Effect.tryPromise(() => createPool(config)),
// Release (guaranteed to run)
(pool) => Effect.promise(() => pool.end()),
);
// Pool is automatically cleaned up, even on errors
const program = Effect.scoped(
Effect.gen(function* () {
const pool = yield* dbPool;
return yield* queryDb(pool, 'SELECT * FROM users');
})
);
Why Effect
| Problem | Without Effect | With Effect |
|---|---|---|
| Error handling | try/catch, unknown type | Typed errors, exhaustive |
| Retries | Manual loops | Effect.retry() |
| Concurrency | Promise.all/race | Structured concurrency |
| Resource cleanup | try/finally | acquireRelease |
| Dependency injection | Frameworks | Built-in Layer system |
| Interruption | Not possible | Cancel running effects |
Get Started
- Documentation
- GitHub — 8K+ stars
- Tutorial
Building reliable data pipelines? My Apify scrapers handle errors and retries. Custom solutions: spinov001@gmail.com
Top comments (0)