Result Types
A Result<T, E> represents either success (ok) or failure (err). It replaces try/catch with explicit typing, giving you compile-time guarantees about error handling.
Why Results?
Section titled “Why Results?”// fetchUser returns AsyncResult<User, 'NOT_FOUND'>const result = await fetchUser('123');
if (result.ok) { console.log(result.value.name);} else { // TypeScript knows result.error is 'NOT_FOUND' console.log(`User ${result.error}`);}try { const user = await fetchUser('123'); console.log(user.name);} catch (error) { // What type is error? TypeScript doesn't know console.log(error);}Results give you:
- Type safety: TypeScript knows exactly what errors can occur
- Explicit handling: Errors are part of the return type, not hidden
- Composability: Results chain together naturally
Type Structure
Section titled “Type Structure” ┌─── The success value type │ ┌─── The error type ▼ ▼Result<Value, Error>
Examples:Result<number, 'DIVIDE_BY_ZERO'> → number on success, 'DIVIDE_BY_ZERO' on failureResult<User, 'NOT_FOUND'> → User on success, 'NOT_FOUND' on failureResult<Order, 'INVALID' | 'EXPIRED'> → Order on success, one of two errors on failureCreating Results
Section titled “Creating Results”import { ok, err, type AsyncResult } from 'awaitly';
// Synchronousconst divide = (a: number, b: number): Result<number, 'DIVIDE_BY_ZERO'> => b === 0 ? err('DIVIDE_BY_ZERO') : ok(a / b);
// Asynchronousconst fetchUser = async (id: string): AsyncResult<User, 'NOT_FOUND'> => { const user = await db.users.find(id); return user ? ok(user) : err('NOT_FOUND');};Checking Results
Section titled “Checking Results”Check result.ok to determine success or failure:
const result = divide(10, 2);
if (result.ok) { console.log(result.value); // 5} else { console.log(result.error); // TypeScript knows this is 'DIVIDE_BY_ZERO'}After checking result.ok, TypeScript narrows the type:
- If
result.okistrue, you can accessresult.value - If
result.okisfalse, you can accessresult.error
Type Guards
Section titled “Type Guards”Use isOk and isErr for functional-style checks:
import { isOk, isErr } from 'awaitly';
const result = divide(10, 0);
if (isOk(result)) { console.log(result.value);}
if (isErr(result)) { console.log(result.error); // 'DIVIDE_BY_ZERO'}Transforming Results
Section titled “Transforming Results”map - Transform the Value
Section titled “map - Transform the Value”Transform the success value, leaving errors unchanged:
import { map } from 'awaitly';
const result = ok(5);const doubled = map(result, (n) => n * 2);// { ok: true, value: 10 }mapError - Transform the Error
Section titled “mapError - Transform the Error”Transform the error, leaving success values unchanged:
import { mapError } from 'awaitly';
const result = err('NOT_FOUND');const mapped = mapError(result, (e) => ({ type: e, status: 404 }));// { ok: false, error: { type: 'NOT_FOUND', status: 404 } }andThen - Chain Operations
Section titled “andThen - Chain Operations”Chain operations that might fail. The function only runs if the previous result was ok:
import { andThen } from 'awaitly';
const result = ok(10);const chained = andThen(result, (n) => n > 0 ? ok(n * 2) : err('NEGATIVE'));// { ok: true, value: 20 }
const negative = ok(-5);const failed = andThen(negative, (n) => n > 0 ? ok(n * 2) : err('NEGATIVE'));// { ok: false, error: 'NEGATIVE' }match - Pattern Match
Section titled “match - Pattern Match”Handle both cases in one expression:
import { match } from 'awaitly';
const result = divide(10, 2);const message = match( result, (value) => `Success: ${value}`, (error) => `Error: ${error}`);// "Success: 5"Unwrapping
Section titled “Unwrapping”unwrap - Get Value or Throw
Section titled “unwrap - Get Value or Throw”import { unwrap } from 'awaitly';
const result = divide(10, 2);const value = unwrap(result); // 5
const badResult = divide(10, 0);const boom = unwrap(badResult); // Throws!unwrapOr - Get Value or Default
Section titled “unwrapOr - Get Value or Default”Return a default value on error:
import { unwrapOr } from 'awaitly';
const result = divide(10, 0);const value = unwrapOr(result, 0); // 0unwrapOrElse - Get Value or Compute Default
Section titled “unwrapOrElse - Get Value or Compute Default”Compute the default from the error:
import { unwrapOrElse } from 'awaitly';
const result = divide(10, 0);const value = unwrapOrElse(result, (error) => { console.log('Failed with:', error); return 0;});// Logs: Failed with: DIVIDE_BY_ZERO// Returns: 0Wrapping Throwing Code
Section titled “Wrapping Throwing Code”from - Wrap Sync Functions
Section titled “from - Wrap Sync Functions”Convert exceptions into Results:
import { from } from 'awaitly';
const result = from(() => JSON.parse('{"valid": true}'));// { ok: true, value: { valid: true } }
const invalid = from(() => JSON.parse('not json'));// { ok: false, error: SyntaxError }fromPromise - Wrap Async Functions
Section titled “fromPromise - Wrap Async Functions”import { fromPromise } from 'awaitly';
const result = await fromPromise(fetch('/api/data'));// Success: { ok: true, value: Response }// Network error: { ok: false, error: TypeError }tryAsync - Wrap with Custom Error
Section titled “tryAsync - Wrap with Custom Error”Convert exceptions into typed errors:
import { tryAsync } from 'awaitly';
const result = await tryAsync( () => fetch('/api/data').then(r => r.json()), (thrown) => ({ type: 'FETCH_FAILED' as const, cause: thrown }));// Success: { ok: true, value: { ...data } }// Failure: { ok: false, error: { type: 'FETCH_FAILED', cause: Error } }Batch Operations
Section titled “Batch Operations”all - Fail Fast
Section titled “all - Fail Fast”Returns the first error encountered, or all values if everything succeeds:
import { all } from 'awaitly';
const results = [ok(1), ok(2), ok(3)];const combined = all(results);// { ok: true, value: [1, 2, 3] }
const withError = [ok(1), err('FAILED'), ok(3)];const failed = all(withError);// { ok: false, error: 'FAILED' }allSettled - Collect All Outcomes
Section titled “allSettled - Collect All Outcomes”Always succeeds, returning every outcome:
import { allSettled } from 'awaitly';
const results = [ok(1), err('A'), ok(3), err('B')];const settled = allSettled(results);// {// ok: true,// value: [// { status: 'ok', value: 1 },// { status: 'err', error: 'A' },// { status: 'ok', value: 3 },// { status: 'err', error: 'B' },// ]// }partition - Separate Successes and Failures
Section titled “partition - Separate Successes and Failures”import { partition } from 'awaitly';
const results = [ok(1), err('A'), ok(3)];const [successes, failures] = partition(results);// successes: [1, 3]// failures: ['A']Async Versions
Section titled “Async Versions”Most operations have async variants for working with promises:
import { allAsync, anyAsync } from 'awaitly';
const results = await allAsync([ fetchUser('1'), fetchPosts('1'), fetchComments('1'),]);// All succeed: { ok: true, value: [User, Post[], Comment[]] }// One fails: { ok: false, error: 'NOT_FOUND' }Flatten
Section titled “Flatten”Unwrap nested Results:
import { flatten, ok, err } from 'awaitly/result';
const nested = ok(ok(42));const flat = flatten(nested);// { ok: true, value: 42 }
const nestedErr = ok(err('INNER'));const flat2 = flatten(nestedErr);// { ok: false, error: 'INNER' }Deserialization
Section titled “Deserialization”Rehydrate Results from JSON, RPC, or server actions with type-safe error handling:
import { deserialize, DESERIALIZATION_ERROR } from 'awaitly/result';
// Valid serialized Resultconst result = deserialize<User, AppError>(rpcResponse);if (result.ok) { console.log(result.value); // User}
// Invalid input returns a typed DeserializationErrorconst invalid = deserialize({ foo: 'bar' });if (!invalid.ok && invalid.error.type === DESERIALIZATION_ERROR) { console.log('Bad input:', invalid.error.value);}Retry with tryAsyncRetry
Section titled “Retry with tryAsyncRetry”Retry async operations without the full workflow engine:
import { tryAsyncRetry } from 'awaitly/result/retry';
const result = await tryAsyncRetry( () => fetch('/api/data').then(r => r.json()), (cause) => ({ type: 'FETCH_FAILED' as const, cause }), { retry: { times: 3, delayMs: 100, backoff: 'exponential', shouldRetry: (e) => e.type === 'FETCH_FAILED', }, });Summary
Section titled “Summary”| Function | Purpose |
|---|---|
ok(value) | Create success Result |
err(error) | Create failure Result |
map(result, fn) | Transform success value |
mapError(result, fn) | Transform error |
andThen(result, fn) | Chain operations |
match(result, onOk, onErr) | Pattern match |
unwrap(result) | Get value or throw |
unwrapOr(result, default) | Get value or default |
from(fn) | Wrap sync throwing code |
fromPromise(promise) | Wrap async throwing code |
tryAsync(fn, mapError) | Wrap with custom error |
tryAsyncRetry(fn, config) | Wrap with retry support |
all(results) | Combine, fail on first error |
partition(results) | Separate successes/failures |
flatten(nested) | Unwrap nested Results |
deserialize(value) | Rehydrate from JSON/RPC |