Result Pattern for TypeScript
Success and failure both travel as plain values the compiler tracks — no throw/catch for predictable failures.
@sandlada/result replaces throw/catch for predictable failures with a discriminated union the
compiler tracks. A function signature tells you whether a call can fail. The library is fully generic
over TError, ships as ESM across 14 subpaths, and has zero dependencies.
Getting startedInstall the package and write your first typed Result pipeline.
Behavior modesWhich operators throw, which capture, and which short-circuit on the first failure.
API referenceEvery export of the published subpaths, generated from the source JSDoc.
Interactive demoRun the examples in StackBlitz without installing anything.
Why a Result type?
Section titled “Why a Result type?”- Failures are part of the signature.
IResultOfT<User, AppError>says a call can fail; a plainUsersays it cannot. There is no documentation to trust and notry/catchto forget. - The compiler proves you handled it.
valueanderrorlive on separate variants, so reading the wrong one is a compile error, not a runtime surprise. - Your error type, not the library’s.
okanderrare generic overTError: discriminated unions, classes, or plain objects all work, and no error is silently coerced toError. - Railway oriented programming without a framework. Data-last curried operators such as
map,bind, andorElsecompose withpipe, so the happy path stays linear.
A complete example
Section titled “A complete example”import type { IResultOfT } from '@sandlada/result';import { ok, err } from '@sandlada/result/factories';import { map, unwrapOr } from '@sandlada/result/operators';import { pipe } from '@sandlada/result/composition';
type User = { id: string; name: string };type AppError = { kind: 'NotFound'; id: string };
function getUser(id: string): IResultOfT<User, AppError> { const user = users.get(id);
return user ? ok(user) : err({ kind: 'NotFound', id });}
const name = pipe( getUser('42'), map((user) => user.name), unwrapOr('Unknown'),);name is 'Alice'. NotFound never becomes an exception — it is a value in the error channel that
the type system forces you to acknowledge.
What ships
Section titled “What ships”- 14 focused subpaths —
factories,operators,option,promise-result,async-result,reliability,observability,primitives, and more, so tree-shaking stays total. - Two async flavors — eager
Promiseoperators and lazyAsyncResult/AsyncOptionthunks. - Reliability built in — bounded
retry,timeout,race,any, andallSettledthat never reject. - Observability built in — breadcrumb
ctx/withPath, formatters, and observer hooks. - Zero dependencies, ESM-only, strict TypeScript, JSON-serializable values.
Install
Section titled “Install”npm i @sandlada/result