Master Type-Safe Error Handling in TypeScript
Practical fp-ts skill for replacing try/catch with Either/TaskEither: chaining, error accumulation, and pattern conversion.
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
Replace exceptions with fp-ts Either and TaskEither patterns.
Implement type-safe error handling for APIs and services.
Learn to chain operations that may fail without nested try/catch blocks.
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.