resultkit@0.4.0
λ resultkit --strict

Type-safe errors with pattern matching
and zero escape hatches.

Strict errors

No unknown escape hatches

Every boundary function requires errorFn. If a function can fail, the caller sees the error type in the signature. Always.

Pattern matching

First-class match()

Standalone match() and matchOn() replace switch and if/else chains. Exhaustive at compile time, safe at runtime.

Enforced workflow

Ships an ESLint preset

Bans throw, try/catch, .catch(), .finally(), switch. The discipline isn't optional.

src/usage.ts Typed
import { ok, err, fromThrowable } from '@valencets/resultkit' import { ResultError } from '@valencets/resultkit' // errorFn required — no unknown leaks const safeParse = fromThrowable( (s: string) => JSON.parse(s), () => new ResultError('PARSE', 'invalid json') ) // Errors short-circuit. Both paths visible in types. const result = safeParse(input) .map(data => data.userId) .andThen(id => findUser(id)) .tap(user => console.log('found:', user.name)) // Object-style match — self-documenting result.match({ ok: user => renderProfile(user), err: e => matchOn(e, 'code', { PARSE: () => show('Bad input'), NOT_FOUND: () => show('User missing'), TIMEOUT: () => retry() }) })
diff — neverthrow vs resultkit compare_arrows
Error types
- fromThrowable(fn) // E = unknown
+ fromThrowable(fn, errorFn) // E typed
Pattern matching
- result.match(okFn, errFn) // only
+ match(status, { OK: ..., ERR: ... })
+ matchOn(err, 'code', { ... })
Escape hatches
- throw / try-catch / .catch() // allowed
+ eslint preset bans all of them
Extras
- // no branded errors, no serialization
+ ResultError<Code> + toJSON / fromJSON
Zero dependencies · ESM only · Node ≥ 22 (oldest LTS with active security support — Node 20 EOL Apr 2026)
pattern-matching.ts Exhaustive
import { match, matchOn } from '@valencets/resultkit' // Exhaustive string-union match type Status = 'active' | 'banned' | 'pending' match(status, { active: () => grantAccess(), banned: () => denyAccess(), pending: () => showVerify() }) // Wildcard — handle some, catch-all the rest match(status, { banned: () => denyAccess(), _: () => grantAccess() }) // Discriminated union — type narrows per handler type AppError = | { code: 'NOT_FOUND'; path: string } | { code: 'TIMEOUT'; ms: number } matchOn(error, 'code', { NOT_FOUND: e => `Missing: ${e.path}`, TIMEOUT: e => `Waited ${e.ms}ms` })
async-combinators.ts Async
import { ResultAsync, combine, partition } from '@valencets/resultkit' // Wrap rejectable promises — errorFn required const fetchUser = (id: number) => ResultAsync.fromPromise( fetch(`/api/users/${id}`).then(r => r.json()), e => new ResultError('FETCH', String(e)) ) // Same API as sync Result const name = await fetchUser(1) .map(u => u.name) .tap(n => console.log('fetched:', n)) .unwrapOr('Unknown') // Tuple-aware combine — infers types + unions errors const both = combine([ fetchUser(1), fetchConfig() ] as const) // Split batch results into [ok[], err[]] const [users, errors] = partition(results)
eslint.config.js — @valencets/resultkit/eslint Strict
import resultkit from '@valencets/resultkit/eslint'

export default [...resultkit.strict]
Banned
throw / try-catch

err() or fromThrowable()

Banned
.catch() / .finally()

ResultAsync.fromPromise()

Banned
switch / enum

match() or matchOn()

Warned
.unwrap() / .unwrapErr()

→ prefer .match() or .unwrapOr()

resultkit.opinionated adds a ban on export default (style rule, unrelated to error handling).
API reference Full docs →
Constructors
ok(value) — success Result
err(error) — failure Result
okAsync(value) — async success
errAsync(error) — async failure
fromThrowable(fn, errFn) — wrap thrower
fromThrowableAsync(fn, errFn)
fromNullable(val, err) — null → Err
ResultAsync.fromPromise(p, errFn)
ResultAsync.fromSafePromise(p)
Transform & terminal
.map(fn) — transform value
.mapErr(fn) — transform error
.andThen(fn) — chain (flatMap)
.orElse(fn) — recover from error
.tap(fn) — inspect value
.tapErr(fn) — inspect error
.match(okFn, errFn) — positional
.match({ ok, err }) — object style
.unwrapOr(default) — safe extract
.unwrap() — extract or throw
.unwrapErr() — extract error or throw
.isOk() / .isErr() — type guards
.toAsync() — sync → async
Combinators & extras
combine(results) — tuple merge
combineAsync(results) — async merge
partition(results) — [ok[], err[]]
flatten(result) — unwrap nested
match(val, handlers) — string union
matchOn(obj, key, handlers)
ResultError<Code> — branded error
isResultError(val) — type guard
resultToJSON(r) — serialize
resultFromJSON(j) — deserialize
InferOkType<R> — extract T
InferErrType<R> — extract E