Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.cbrock.dev/llms.txt

Use this file to discover all available pages before exploring further.

ResultAsync<T, E> is a PromiseLike<Result<T, E>>. You can await or .then() it directly to get a synchronous Result<T, E>, or chain methods on it without ever leaving the async context. Nearly Every method that exists on Result has an equivalent on ResultAsync, and each returns another ResultAsync so chains stay fluent.
import { chas } from 'ts-chas';

const result: chas.Result<number, string> = await chas.okAsync(42);
// Ok(42)

Creating a ResultAsync

chas.okAsync(value)

Wraps a value or Promise<T> in a ResultAsync that resolves to Ok.
value
T | Promise<T>
required
The success value or promise to wrap.
returns
ResultAsync<T, never>
Resolves to Ok(value).
const res = chas.okAsync(42);
const result = await res; // Ok(42)

chas.errAsync(error)

Wraps an error or Promise<E> in a ResultAsync that resolves to Err.
error
E | Promise<E>
required
The error value or promise to wrap.
returns
ResultAsync<never, E>
Resolves to Err(error).
const res = chas.errAsync('request failed');
const result = await res; // Err('request failed')

chas.fromPromise(promise, mapErr?)

Wraps a native Promise in a ResultAsync. Resolves to Ok on fulfillment and Err on rejection.
promise
Promise<T>
required
The promise to wrap.
mapErr
(error: unknown) => E
Maps the rejection reason to your error type. When omitted, the error is typed as unknown.
returns
ResultAsync<T, E>
A ResultAsync wrapping the promise outcome.
const res = chas.fromPromise(
  fetch('/api/user').then(r => r.json()),
  (e) => `Fetch failed: ${e}`
);

ResultAsync.fromResult(syncResult)

Lifts a synchronous Result into ResultAsync.
syncResult
Result<T, E>
required
The synchronous result to wrap.
returns
ResultAsync<T, E>
A ResultAsync that immediately resolves to the provided result.
const sync = chas.ok(42);
const async = chas.ResultAsync.fromResult(sync);
const result = await async; // Ok(42)

ResultAsync.fromSafePromise(promise)

Wraps a promise that is guaranteed not to reject. No mapErr is needed.
promise
Promise<T>
required
A promise that will not reject.
returns
ResultAsync<T, never>
Resolves to Ok(value).
const res = chas.ResultAsync.fromSafePromise(Promise.resolve(42));
const result = await res; // Ok(42)

Instance methods

All methods below return a ResultAsync, so you can chain them without await until you need the final value.

.map(fn)

Transforms the Ok value. Err passes through unchanged.
fn
(value: T) => U | Promise<U>
required
Function applied to the Ok value. May be async.
returns
ResultAsync<U, E>
Ok(fn(value)) or the original Err.
const res = chas.okAsync(5).map(v => v * 2);
const result = await res; // Ok(10)

.mapErr(fn)

Transforms the Err error. Ok passes through unchanged.
fn
(error: E) => F | Promise<F>
required
Function applied to the Err error. May be async.
returns
ResultAsync<T, F>
The original Ok or Err(fn(error)).
const res = chas.errAsync('not_found').mapErr(e => e.toUpperCase());
const result = await res; // Err('NOT_FOUND')

.andThen(fn)

Chains another Result- or ResultAsync-returning function. Calls fn with the Ok value, or short-circuits on Err.
fn
(value: T) => Result<U, F> | ResultAsync<U, F>
required
Function returning a new result. May return sync or async.
returns
ResultAsync<U, E | F>
The chained result, or the original Err.
const res = chas.okAsync(5).andThen(v => chas.okAsync(v * 2));
const result = await res; // Ok(10)

.orElse(fn)

Recovers from an Err by calling fn with the error. Ok passes through unchanged.
fn
(error: E) => Result<T2, F> | ResultAsync<T2, F>
required
Recovery function returning a new result.
returns
ResultAsync<T | T2, F>
The original Ok, or the recovery result.
const res = chas.errAsync('timeout').orElse(() => chas.okAsync('default'));
const result = await res; // Ok('default')

.tap(fn)

Runs a side effect when Ok. Returns the original result unchanged.
fn
(value: T) => void | Promise<void>
required
Side-effect function. May be async.
returns
ResultAsync<T, E>
The original result, unmodified.
const res = await fetchUser()
  .tap(async u => await auditLog.record(u.id));

.tapErr(fn)

Runs a side effect when Err. Returns the original result unchanged.
fn
(error: E) => void | Promise<void>
required
Side-effect function. May be async.
returns
ResultAsync<T, E>
The original result, unmodified.
const res = await fetchUser()
  .tapErr(async e => await errorLog.send(e));

.match({ ok, err })

Exhaustively handles both branches and resolves to a plain value.
ok
(value: T) => U | Promise<U>
required
Handler called when the result is Ok.
err
(error: E) => F | Promise<F>
required
Handler called when the result is Err.
returns
Promise<U | F>
A standard Promise (not a ResultAsync) resolving to the matched handler’s return value.
const message = await chas.okAsync(42).match({
  ok: v => `Got ${v}`,
  err: e => `Failed: ${e}`,
});
// 'Got 42'

.unwrap()

Returns a Promise that resolves to the Ok value, or throws the Err error.
returns
Promise<T>
Resolves to the Ok value.
Throws the contained error if the result is Err. Prefer .unwrapOr() or .match() in production code.
const value = await chas.okAsync(5).unwrap(); // 5

.unwrapOr(defaultValue)

Returns a Promise that resolves to the Ok value, or defaultValue if Err.
defaultValue
T
required
Fallback value returned when the result is Err.
returns
Promise<T>
Resolves to the Ok value or the default.
const value = await chas.errAsync('missing').unwrapOr(0); // 0

.pipe(fn1, fn2, ...)

Passes the ResultAsync through a sequence of functions, identical in behaviour to Result#pipe.
const add5 = (r: chas.ResultAsync<number, never>) => r.map(v => v + 5);
const double = (r: chas.ResultAsync<number, never>) => r.map(v => v * 2);

const result = await chas.okAsync(1).pipe(add5, double);
// Ok(12)

.context(ctx)

Attaches a debug label or metadata object to the error when Err. No-op when Ok.
ctx
string | Record<string, unknown>
required
A description string or metadata object for the current step.
returns
ResultAsync<T, E>
The same result with context prepended to error._context (if Err).
const result = await fetchUser(id)
  .context('fetching user')
  .andThen(u => validatePermissions(u))
  .context('checking permissions');

.catchTag(target, handler)

Catches a specific tagged error variant by _tag or error factory, and removes it from the error union. Unmatched tags pass through unchanged.
target
string | ErrorFactory
required
The _tag string to match, or an error factory.
handler
(error: MatchedError) => Result<T2, E2> | ResultAsync<T2, E2>
required
Recovery function.
returns
ResultAsync<T | T2, Exclude<E, { _tag: ... }> | E2>
A result with the matched tag removed from the error union.
await fetchUser(id)
  .catchTag('NotFound', () => chas.ok(GUEST_USER));
// ResultAsync<User | GuestUser, Unauthorized>

.tapTag(target, handler)

Runs a side effect for a specific tagged error. The original result is returned unchanged.
target
string | ErrorFactory
required
The _tag string to match, or an error factory.
handler
(error: MatchedError) => void | Promise<void>
required
Side-effect function called when the tag matches.
returns
ResultAsync<T, E>
The original result, unmodified.
await fetchUser(id)
  .tapTag('Unauthorized', () => redirectToLogin());

Complete example

import { chas } from 'ts-chas';

interface User { id: number; name: string }

function fetchUser(id: number): chas.ResultAsync<User, string> {
  return chas.fromPromise(
    fetch(`/api/users/${id}`).then(r => {
      if (!r.ok) throw new Error(`HTTP ${r.status}`);
      return r.json() as Promise<User>;
    }),
    (e) => `Fetch error: ${e}`
  );
}

const result = await fetchUser(1)
  .map(user => ({ ...user, name: user.name.toUpperCase() }))
  .mapErr(e => ({ code: 'FETCH_FAILED', detail: e }))
  .tap(user => console.log('Fetched:', user.name));

result.match({
  ok: user => console.log('User:', user.name),  // 'ALICE'
  err: e => console.error('Failed:', e.detail),
});