Skill

Master Type-Safe Error Handling in TypeScript

Practical fp-ts skill for replacing try/catch with Either/TaskEither: chaining, error accumulation, and pattern conversion.


85
Spark score
out of 100
Updated 15 days ago
Version 14.5.0

Add to Favorites

Why it matters

Eliminate 'try/catch spaghetti' and embrace robust, type-safe error handling in your TypeScript applications. Learn practical patterns using fp-ts to manage errors as data, improving code clarity and reliability.

Outcomes

What it gets done

01

Replace exceptions with fp-ts Either and TaskEither patterns.

02

Implement type-safe error handling for APIs and services.

03

Learn to chain operations that may fail without nested try/catch blocks.

04

Collect and manage multiple validation errors effectively.

Install

Add it to your toolbox

Run in your project directory:

curl -fsSL https://spark.entire.vc/get/ag-fp-ts-errors | bash

Overview

Practical Error Handling with fp-ts

A practical fp-ts skill for TypeScript error handling: replacing try/catch with Either/TaskEither, chaining fallible operations, accumulating validation errors, and converting between throwing/Promise/Either patterns. Use for type-safe TypeScript error handling with Either/TaskEither, especially when accumulating multiple validation errors or building explicit error-typed APIs.

What it does

Practical Error Handling with fp-ts teaches replacing try/catch with type-safe Either/TaskEither patterns - errors as data instead of exceptions the type system can't see. Either<E, A> holds a Left (error) or Right (success); throwing code is wrapped at the boundary with E.tryCatch/E.tryCatchK, and common operations (E.map, E.mapLeft, E.fold, E.getOrElse, E.fromNullable) replace manual null/error checks.

When to use - and when NOT to

Use it for type-safe error handling in TypeScript, replacing try/catch with Either/TaskEither, building APIs needing explicit error types, or accumulating multiple validation errors instead of stopping at the first. Chained operations use E.chain/E.filterOrElse, with E.Do/E.bind for readable multi-step pipelines and E.chainW ("wider") to automatically union differing error types across steps rather than forcing a single error type. For async operations, TaskEither<E, A> mirrors Either over a lazy Promise, chained the same way, pattern-matched at the end with TE.fold, and parallelized via sequenceS(TE.ApplyPar).

Inputs and outputs

Collecting all errors (e.g. form validation) uses E.getApplicativeValidation over a NonEmptyArray semigroup combined with sequenceS, so every field's error surfaces at once instead of stopping at the first failure - extendable to field-level errors carrying which field failed. Resilience patterns include exponential-backoff retry and cache-then-API fallback chains:

import * as T from 'fp-ts/Task'
import * as TE from 'fp-ts/TaskEither'
import { pipe } from 'fp-ts/function'

const retry = <E, A>(
  task: TE.TaskEither<E, A>,
  attempts: number,
  delayMs: number
): TE.TaskEither<E, A> =>
  pipe(
    task,
    TE.orElse((error) =>
      attempts > 1
        ? pipe(
            T.delay(delayMs)(T.of(undefined)),
            T.chain(() => retry(task, attempts - 1, delayMs * 2))
          )
        : TE.left(error)
    )
  )

// Retry up to 3 times with exponential backoff
const fetchWithRetry = retry(fetchUser('123'), 3, 1000)

Real-world worked scenarios include safely parsing untyped input field-by-field, a full API-error type with HTTP-status-aware handling, processing a list where some items fail (collecting successes and failures separately), and bulk operations reporting partial success.

Integrations

Conversion utilities bridge real codebases that mix styles: nullable values via E.fromNullable/E.fromOption, throwing libraries (like Zod) wrapped with E.tryCatch at the boundary, Promise-based libraries (fetch, axios, Prisma) wrapped as TaskEither via TE.tryCatch, and an escape hatch back to plain Promises (awaiting the Either directly, converting to a throwing Promise for legacy code, or defaulting on error via TE.getOrElse) for interop with external APIs that expect exceptions.

Who it's for

TypeScript developers who want composable, type-tracked error handling - explicit error types the compiler enforces, chains that stop cleanly at the first failure or accumulate every failure when needed, and disciplined conversion points at the edges of a codebase that still throws.

FAQ

Common questions

Discussion

Questions & comments · 0

Sign In Sign in to leave a comment.