DEV Community

Alex Spinov
Alex Spinov

Posted on

Effect Has a Free TypeScript Standard Library — Here's How to Use It

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
Enter fullscreen mode Exit fullscreen mode
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);
Enter fullscreen mode Exit fullscreen mode

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!
);
Enter fullscreen mode Exit fullscreen mode

Retries

const fetchData = pipe(
  Effect.tryPromise(() => fetch('/api/data')),
  Effect.retry({
    times: 3,
    schedule: Schedule.exponential('1 second'),
  }),
);
Enter fullscreen mode Exit fullscreen mode

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(),
]);
Enter fullscreen mode Exit fullscreen mode

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');
  })
);
Enter fullscreen mode Exit fullscreen mode

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


Building reliable data pipelines? My Apify scrapers handle errors and retries. Custom solutions: spinov001@gmail.com

Top comments (0)