Result

A Result is either Ok (a success, contains a value of type T) or Err (an error, contains an error of type E):

Result<T, E> = Ok<T> | Err<E>

Necessary imports:

import { Err, Ok, Result } from 'ts-results-es'

Construction:

const success = Ok('my username')
const error = Err('error_code')

Result can only be combined with synchronous code. Look at AsyncResult if you need to combine results with asynchronouse code.

all()

// Array overload
static all(results: Result<T, E>[]): Result<T[], E>

// Object overload (accepts any object type with Result values, preserving per-key types)
static all<T extends Record<string, Result<any, any>>>(results: T): Result<ResultOkTypesRecord<T>, Partial<ResultErrTypesRecord<T>>>

Parse a set of Result, returning an array of all Ok values. Short circuits with the first Err found, if any.

When called with an object:

Parse an object of Results, returning an object of all Ok values. If any Result is Err, returns an Err containing an object of all errors (only keys that were Err are present). Unlike the array variant, it does not short-circuit and collects all errors.

Array example:

let results: Result<Topping, GetToppingsError>[] = pizzaToppingNames.map(name => getPizzaToppingByName(name));

let result = Result.all(results); // Result<Topping[], GetToppingsError>

let toppings = result.unwrap(); // toppings is an array of Topping.  Could throw GetToppingsError.

Object example:

let result = Result.all({
    name: validateName(input.name),  // Result<string, NameError>
    age: validateAge(input.age),     // Result<number, AgeError>
});
// Ok({ name: 'Alice', age: 25 }),
// type: Result<{ name: string; age: number }, Partial<{ name: NameError; age: AgeError }>>

andThen()

andThen<T2, E2>(mapper: (val: T) => Result<T2, E2>): Result<T | T2, E | E2>

Calls mapper if the result is Ok, otherwise returns the Err value of self. This function can be used for control flow based on Result values.

Example:

let goodResult = Ok(1);
let badResult = Err(new Error('something went wrong'));

goodResult.andThen((num) => new Ok(num + 1)).unwrap(); // 2
badResult.andThen((num) => new Err(new Error('2nd error'))).unwrap(); // throws Error('something went wrong')
goodResult.andThen((num) => new Err(new Error('2nd error'))).unwrap(); // throws Error('2nd error')

goodResult
    .andThen((num) => new Ok(num + 1))
    .mapErr((err) => new Error('mapped'))
    .unwrap(); // 2
badResult
    .andThen((num) => new Err(new Error('2nd error')))
    .mapErr((err) => new Error('mapped'))
    .unwrap(); // throws Error('mapped')
goodResult
    .andThen((num) => new Err(new Error('2nd error')))
    .mapErr((err) => new Error('mapped'))
    .unwrap(); // throws Error('mapped')

any()

// The actual signature is more complicated but this should be good enough.
static any(results: Result<T, E>[]): Result<T, E[]>

Parse a set of Result, short-circuits when an input value is Ok. If no Ok is found, returns an Err containing the collected error values.

Example:

let connections: Array<Result<string, Error1 | Error2 | Error3 | ...>> = [attempt1(), attempt2(), attempt3(), ...];

let results = Result.any(connections); // Result<string, Error1 | Error2 | Error3 | ...>

let url = results.unwrap(); // At least one attempt gave us a successful url

partition()

// The actual signature is more complicated but this should be good enough.
static partition<T, E>(results: Result<T, E>[]): [T[], E[]]

Partitions a set of Result, separating the Ok and Err values.

Example:

let results: Result<number, string>[] = [Ok(1), Err('error1'), Ok(2), Err('error2')];

let [numbers, errors] = Result.partition(results); // [ [1, 2], ['error1', 'error2'] ]

Err.EMPTY

static readonly EMPTY: Err<void>

A static Err instance containing undefined. Useful when you need to represent a failure without a meaningful error value.

Example:

const x: Result<string, void> = Err.EMPTY

Ok.EMPTY

static readonly EMPTY: Ok<void>

A static Ok instance containing undefined. Useful when you need to represent a success without a meaningful value.

Example:

const x: Result<void, string> = Ok.EMPTY

ResultErrType

type ResultErrType<T extends Result<any, any>>

A utility type that extracts the Err value type from a Result.

Example:

type Input = Result<string, Error>
type Output = ResultErrType<Input> // Error

ResultErrTypes

type ResultErrTypes<T extends Result<any, any>[]>

A utility type that extracts the Err value types from a tuple of Result’s, producing a tuple of the error types.

Example:

type Input = [Result<string, Error>, Result<number, Error>]
type Output = ResultErrTypes<Input> // [Error, Error]

ResultOkType

type ResultOkType<T extends Result<any, any>>

A utility type that extracts the Ok value type from a Result.

Example:

type Input = Result<string, Error>
type Output = ResultOkType<Input> // string

ResultOkTypes

type ResultOkTypes<T extends Result<any, any>[]>

A utility type that extracts the Ok value types from a tuple of Result’s, producing a tuple of the inner types.

Example:

type Input = [Result<string, Error>, Result<number, Error>]
type Output = ResultOkTypes<Input> // [string, number]

ResultOkTypesRecord

type ResultOkTypesRecord<T extends Record<string, Result<any, any>>>

A utility type that extracts the Ok value types from an object of Results, producing an object of the inner types.

Example:

type Input = { name: Result<string, Error>; age: Result<number, Error> }
type Output = ResultOkTypesRecord<Input> // { name: string; age: number }

ResultErrTypesRecord

type ResultErrTypesRecord<T extends Record<string, Result<any, any>>>

A utility type that extracts the Err value types from an object of Results, producing an object of the error types.

Example:

type Input = { name: Result<string, Error>; age: Result<number, TypeError> }
type Output = ResultErrTypesRecord<Input> // { name: Error; age: TypeError }

error

The error contained in Err. Only present on Err objects.

value

The value contained in Ok. Only present on Ok objects.

expect()

expect(msg: string): T

Returns the contained Ok value, if exists. Throws an error if not.

The thrown error’s cause is set to value contained in Err.

If you know you’re dealing with Ok and the compiler knows it too (because you tested isOk() or isErr()) you should use value instead. While Ok’s expect() and value will both return the same value using value is preferable because it makes it clear that there won’t be an exception thrown on access.

msg: the message to throw if no Ok value.

Example:

let goodResult = Ok(1);
let badResult = Err(new Error('something went wrong'));

goodResult.expect('goodResult should be a number'); // 1
badResult.expect('badResult should be a number'); // throws Error("badResult should be a number - Error: something went wrong")

expectErr()

expectErr(msg: string): E

Returns the contained Err value, if exists. Throws an error if not.

msg: the message to throw if no Err value

Example:

let goodResult = Ok(1);
let badResult = Err(new Error('something went wrong'));

goodResult.expectErr('goodResult should not be a number'); // throws Error("goodResult should not be a number")
badResult.expectErr('badResult should not be a number'); // new Error('something went wrong')

isOk()

isOk(): this is Ok<T>

true when the result is Ok.

isErr()

isErr(): this is Err<E>

true when the result is Err.

map()

map<U>(mapper: (val: T) => U): Result<U, E>

Maps a Result<T, E> to Result<U, E> by applying a function to a contained Ok value, leaving an Err value untouched.

This function can be used to compose the results of two functions.

Example:

let goodResult = Ok(1);
let badResult = Err(new Error('something went wrong'));

goodResult.map((num) => num + 1).unwrap(); // 2
badResult.map((num) => num + 1).unwrap(); // throws Error("something went wrong")

mapErr()

mapErr<F>(mapper: (val: E) => F): Result<T, F>

Maps a Result<T, E> to Result<T, F> by applying a function to a contained Err value, leaving an Ok value untouched.

This function can be used to pass through a successful result while handling an error.

Example:

let goodResult = Ok(1);
let badResult = Err(new Error('something went wrong'));

goodResult
    .map((num) => num + 1)
    .mapErr((err) => new Error('mapped'))
    .unwrap(); // 2
badResult
    .map((num) => num + 1)
    .mapErr((err) => new Error('mapped'))
    .unwrap(); // throws Error("mapped")

mapOr()

mapOr<U>(default_: U, mapper: (val: T) => U): U

Maps a Result<T, E> to Result<U, E> by either converting T to U using mapper (in case of Ok) or using the default_ value (in case of Err).

If default_ is a result of a function call consider using mapOrElse() instead, it will only evaluate the function when needed.

Example:

let goodResult = Ok(1);
let badResult = Err(new Error('something went wrong'));

goodResult.mapOr(0, (value) => -value) // -1
badResult.mapOr(0, (value) => -value) // 0

mapOrElse()

mapOrElse<U>(default_: (error: E) => U, mapper: (val: T) => U): U

Maps a Result<T, E> to Result<U, E> by either converting T to U using mapper (in case of Ok) or producing a default value using the default_ function (in case of Err).

let goodResult = Ok(1);
let badResult = Err(new Error('something went wrong'));

goodResult.mapOrElse((_error) => 0, (value) => -value) // -1
badResult.mapOrElse((_error) => 0, (value) => -value) // 0

or()

or<E2>(other: Result<T, E2>): Result<T, E2>

Returns Ok() if we have a value, otherwise returns other.

other is evaluated eagerly. If other is a result of a function call try orElse() instead – it evaluates the parameter lazily.

Example:

Ok(1).or(Ok(2)) // => Ok(1)
Err('error here').or(Ok(2)) // => Ok(2)

orElse()

orElse<E2>(other: (error: E) => Result<T, E2>): Result<T, E2>

Returns Ok() if we have a value, otherwise returns the result of calling other().

other() is called only when needed and is passed the error value in a parameter.

Example:

Ok(1).orElse(() => Ok(2)) // => Ok(1)
Err('error').orElse(() => Ok(2)) // => Ok(2)

stack

A stack trace is generated when an Err is created.

let error = Err('Uh Oh');
let stack = error.stack;

toAsyncResult()

toAsyncResult(): AsyncResult<T, E>

Creates an AsyncResult based on this Result.

Useful when you need to compose results with asynchronous code.

toOption()

toOption(): Option<T>

Converts from Result<T, E> to Option<T> , discarding the error if any.

unwrap()

unwrap(): T

Returns the contained Ok value. Because this function may throw, its use is generally discouraged. Instead, prefer to handle the Err case explicitly.

If you know you’re dealing with Ok and the compiler knows it too (because you tested isOk() or isErr()) you should use value instead. While Ok’s unwrap() and value will both return the same value using value is preferable because it makes it clear that there won’t be an exception thrown on access.

Throws if the value is an Err, with a message provided by the Err’s value and cause set to the value.

Example:

let goodResult = new Ok(1);
let badResult = new Err(new Error('something went wrong'));

goodResult.unwrap(); // 1
badResult.unwrap(); // throws Error("something went wrong")

unwrapErr()

unwrapErr(): E

Returns the contained Err value. Because this function may throw, its use is generally discouraged. Instead, prefer to handle the Ok case explicitly and access the error property directly.

Throws if the value is an Ok, with a message provided by the Ok’s value and cause set to the value.

Example:

let goodResult = new Ok(1);
let badResult = new Err('something went wrong');

goodResult.unwrapErr(); // throws an exception
badResult.unwrapErr(); // returns 'something went wrong'

unwrapOr()

unwrapOr<T2>(val: T2): T | T2

Returns the contained Ok value or a provided default.

Example:

let goodResult = Ok(1);
let badResult = Err(new Error('something went wrong'));

goodResult.unwrapOr(5); // 1
badResult.unwrapOr(5); // 5

unwrapOrElse()

unwrapOrElse<T2>(f: (error: E) => T2): T | T2

Returns the contained Ok value or computes a value with a provided function.

The function is called at most one time, only if needed.

Example:

Ok('OK').unwrapOrElse(
    (error) => { console.log(`Called, got ${error}`); return 'UGH'; }
) // => 'OK', nothing printed

Err('A03B').unwrapOrElse((error) => `UGH, got ${error}`) // => 'UGH, got A03B'

wrap()

static wrap<T, E = unknown>(op: () => T): Result<T, E>

Wraps an operation that may throw an error (try-catch style) into a Result.

This is useful for converting exception-based code into Result-based code.

Example:

Result.wrap(() => JSON.parse('{"valid": "json"}')) // Ok({ valid: 'json' }), type: Result<any, unknown>

Result.wrap(() => JSON.parse('not json')) // Err(SyntaxError: ...), type: Result<any, unknown>

wrapAsync()

static wrapAsync<T, E = unknown>(op: () => Promise<T>): Promise<Result<T, E>>

Wraps an async operation that may throw an error (try-catch style) into a Result.

This is useful for converting promise-based code that may reject into Result-based code.

Example:

await Result.wrapAsync(() => fetch('/api/data').then(r => r.json())) // Ok(data) or Err(error), type: Result<any, unknown>

Iterable

Result implements the Iterable interface, allowing you to use it in for...of loops and with spread syntax.

  • Ok<T> yields its contained value once

  • Err<E> yields nothing (empty iterator)

Example:

for (const value of Ok(42)) {
    console.log(value); // 42
}

for (const value of Err('error')) {
    console.log(value); // never executes
}

[...Ok(1)] // [1], type: number[]
[...Err('oops')] // [], type: never[]

const results = [Ok(1), Err('bad'), Ok(3)];
results.flatMap(res => [...res]); // [1, 3], type: number[]