PendingResult
PendingResult
represents a
Result
, whose value is not yet resolved (thus, pending).
PendingResult
implements PromiseLike<Result<T, E>>
interface, which means that
it can be Awaited
as a regular Promise
.
In order to access the value of PendingResult
, you need to await it, which
will resolve it to either Ok<T>
or Err<E>
variant. Once resolved, you can
use the methods available on Result
to work with the value.
Constructors
pendingErr(error: E | CheckedError<E> | Promise<E> | Promise<CheckedError<E>>)
- creates aPendingResult
that resolves toErr<E>
variant.pendingOk(value: T | Promise<T>)
- creates aPendingOption
that resolves toSome<T>
variant.pendingResult(resultOrFactory: | Result<T, E> | Promise<Result<T, E>> | (() => Result<T, E> | Promise<Result<T, E>>))
- creates aPendingOption
that resolves to the providedResult
.
Methods
and
Returns a PendingResult
that resolves to Err
if this result resolves to Err
,
otherwise returns a PendingResult
with x
.
This is the asynchronous version of and
.
If x
is a Promise
and rejects, None
is returned.
const x = ok<number, string>(1).toPending();
const y = ok<number, string>(2);
const z = err<number, string>("failure").toPending();
expect(await x.and(y)).toStrictEqual(ok(2));
expect(await x.and(z)).toStrictEqual(err("failure"));
expect(await z.and(x)).toStrictEqual(err("failure"));
andThen
Returns a PendingResult
that resolves to Err
if this result resolves to Err
,
otherwise applies f
to the resolved Ok
value and returns its result.
This is the asynchronous version of andThen
.
const x = ok<number, string>(2).toPending();
const y = err<number, string>("failure").toPending();
expect(await x.andThen((n) => ok(n * 2))).toStrictEqual(ok(4));
expect(await x.andThen((_) => err("oops"))).toStrictEqual(err("oops"));
expect(await y.andThen((_) => err("oops"))).toStrictEqual(err("failure"));
check
check(): Promise<[boolean, Awaited<T> | CheckedError<Awaited<E>>]>
Inspects this PendingResult
’s state, returning a promise of
a tuple with a success flag and either the value or error.
This is the asynchronous version of check
.
- Resolves to
[true, Awaited<T>]
if this is anOk
, or to[false, CheckedError<Awaited<E>>]
if this is anErr
. - Never rejects, providing a safe way to await the result’s state.
const x = ok<number, string>(42).toPending();
const y = err<number, string>("failure").toPending();
expect(await x.check()).toEqual([true, 42]);
expect(await y.check()).toEqual([false, expect.objectContaining({ expected: "failure" })]);
combine
Combines this PendingResult
with other Result
or PendingResult
instances into a single PendingResult
containing a tuple of resolved values.
The combine
method takes an arbitrary number of Result
or PendingResult
instances. It resolves all inputs and returns a PendingResult
that, when
resolved, contains a Result
with a tuple of their Ok
values if all
resolve to Ok
. If any input resolves to Err
, the result resolves to
that Err
. The resulting tuple includes the resolved value of this PendingResult
as the first element, followed by the resolved values from the provided instances.
const a = pendingOk<number, Error>(1);
const b = ok<Promise<string>, Error>(Promise.resolve("hi"));
const c = err<symbol, Error>(new Error("An error occurred"));
const d = pendingErr<Promise<Date>, Error>(new Error("not a date"));
const e = a.combine(b, c, d); // PendingResult<[number, string, symbol, Date], Error>
err
err(): PendingOption<Awaited<E>>
Converts this PendingResult
to a PendingOption
containing the awaited error, if present.
Returns a PendingOption
that resolves to Some
with the error value if this resolves to an Err
with ExpectedError
,
or to None
if this resolves to an Ok
or Err
with UnexpectedError
.
This is the asynchronous version of err
.
const x = ok<number, string>(1).toPending();
const y = err<number, string>("failure").toPending();
expect(await x.err()).toStrictEqual(none());
expect(await y.err()).toStrictEqual(some("failure"));
flatten
Flattens a nested PendingResult
into a single pending result,
resolving any inner Result
or PendingResult
to its final state.
This is the asynchronous version of flatten
.
const x = ok(ok(6)).toPending();
const y = ok(err<number, string>("oops")).toPending();
expect(await x.flatten()).toStrictEqual(ok(6));
expect(await y.flatten()).toStrictEqual(err("oops"));
inspect
inspect(f: (x: T) => unknown): PendingResult<T, E>
Calls f
with the value if this pending result resolves to an Ok
,
then returns a new pending result with the original state.
This is the asynchronous version of inspect
.
- Returns a new
PendingResult
instance, not the original reference. - If
f
throws or returns aPromise
that rejects, the error is ignored, and the returned promise still resolves to the original state.
const x = ok<number, string>(2).toPending();
const y = err<number, string>("failure").toPending();
let sideEffect = 0;
expect(await x.inspect(n => (sideEffect = n))).toStrictEqual(ok(2));
expect(await x.inspect(_ => { throw new Error() })).toStrictEqual(ok(2));
expect(sideEffect).toBe(2);
expect(await y.inspect(n => (sideEffect = n))).toStrictEqual(err("failure"));
expect(sideEffect).toBe(2); // Unchanged
inspectErr
inspectErr(f: (x: CheckedError<E>) => unknown): PendingResult<T, E>
Calls f
with the error if this pending result resolves to an Err
,
then returns a new pending result with the original state.
This is the asynchronous version of inspectErr
.
- Returns a new
PendingResult
instance, not the original reference. - If
f
throws or returns aPromise
that rejects, the error is ignored, and the returned promise still resolves to the original state.
const x = ok<number, string>(2).toPending();
const y = err<number, string>("failure").toPending();
let sideEffect: CheckedError<string> | null = null;
expect(await x.inspectErr(n => (sideEffect = n))).toStrictEqual(ok(2));
expect(await x.inspectErr(_ => { throw new Error() })).toStrictEqual(ok(2));
expect(sideEffect).toBeNull();
expect(await y.inspectErr(n => (sideEffect = n))).toStrictEqual(err("failure"));
expect(await y.inspectErr(_ => { throw new Error() })).toStrictEqual(err("failure"));
expect(isCheckedError(sideEffect)).toBe(true);
iter
iter(): AsyncIterableIterator<Awaited<T>, Awaited<T>, void>
Returns an async iterator over this pending result’s value, yielding it if
it resolves to Ok
or nothing if it resolves to Err
.
- Yields exactly one item for a resolved
Ok
, or zero items for a resolvedErr
. - Compatible with
for await...of
loops and async spread operators (with caution). - Ignores the error value in
Err
cases, focusing only on the success case.
const x = ok<number, string>(42).toPending();
const y = err<number, string>("failure").toPending();
const iterX = x.iter();
expect(await iterX.next()).toEqual({ value: 42, done: false });
expect(await iterX.next()).toEqual({ done: true });
const iterY = y.iter();
expect(await iterY.next()).toEqual({ done: true });
async function collect(iter: AsyncIterableIterator<number, number, void>) {
const result = [];
for await (const val of iter) result.push(val);
return result;
}
expect(await collect(x.iter())).toEqual([42]);
expect(await collect(y.iter())).toEqual([]);
map
map<U>(f: (x: T) => U): PendingResult<Awaited<U>, Awaited<E>>
Maps the resolved value with f
, returning a PendingResult
with
the result if Ok
, or the original Err
if Err
.
This is the asynchronous version of map
.
If f
throws or returns a rejected promise, returns a PendingResult
with an Err
containing an UnexpectedError
.
const x = ok<number, string>(2).toPending();
const y = err<number, string>("failure").toPending();
expect(await x.map(n => n * 2)).toStrictEqual(ok(4));
expect((await x.map(() => { throw new Error("boom") })).unwrapErr().unexpected).toBeDefined();
expect(await y.map(n => n * 2)).toStrictEqual(err("failure"));
mapAll
Maps this pending result by applying a callback to its full state,
executing the callback for both Ok
and Err
, returning
a new PendingResult
.
Unlike andThen
, which only invokes the callback for Ok
,
this method always calls f
, passing the entire Result
as its argument.
This is the asynchronous version of mapAll
.
If f
throws or returns a Promise
that rejects, the newly created PendingResult
will resolve to an Err
with an UnexpectedError
.
const okRes = ok<number, string>(42).toPending();
const errRes = err<number, string>("failure").toPending();
const okMapped = okRes.mapAll(res => Promise.resolve(ok(res.unwrapOr(0) + 1)));
expect(await okMapped).toStrictEqual(ok(43));
const errMapped = errRes.mapAll(res => Promise.resolve(ok(res.unwrapOr(0) + 1)));
expect(await errMapped).toStrictEqual(ok(1));
const throwMapped = okRes.mapAll(() => { throw new Error("boom") });
expect((await throwMapped).unwrapErr().unexpected).toBeDefined();
mapErr
mapErr<F>(f: (x: E) => F): PendingResult<Awaited<T>, Awaited<F>>
Transforms this pending result by applying f
to the error if it resolves
to an Err
with an expected error, or preserves the Ok
unchanged.
This is the asynchronous version of mapErr
.
- If
f
throws or returns a rejected promise, returns aPendingResult
with anErr
containing anUnexpectedError
. - If this resolves to an
Err
with anUnexpectedError
,f
is not called, and the original error is preserved.
const x = ok<number, string>(2).toPending();
const y = err<number, string>("failure").toPending();
expect(await x.mapErr(e => e.length)).toStrictEqual(ok(2));
expect(await y.mapErr(e => e.length)).toStrictEqual(err(7));
expect((await y.mapErr(() => { throw new Error("boom") })).unwrapErr().unexpected).toBeDefined();
match
match<U, F = U>(f: (x: T) => U, g: (e: CheckedError<E>) => F): Promise<Awaited<U | F>>
Returns this pending result if it resolves to an Ok
, otherwise returns x
.
This is the asynchronous version of match
.
If f
or g
throws or returns a rejected Promise
, the returned promise rejects with
the original error. In this case the caller is responsible for handling the rejection.
Rejects with ResultError
if f
or g
throws an exception or rejects,
original error will be set as ResultError.reason
.
const x = ok<number, string>(2).toPending();
const y = err<number, string>("failure").toPending();
expect(await x.match(n => n * 2, () => 0)).toBe(4);
expect(await y.match(n => n * 2, e => e.expected?.length)).toBe(7);
or
Returns this pending result if it resolves to an Ok
, otherwise returns x
.
This is the asynchronous version of or
.
If this result resolves to an Err
and x
is a Promise
that rejects, the resulting
PendingResult
resolves to an Err
with an UnexpectedError
.
const x = ok<number, string>(2).toPending();
const y = err<number, string>("failure").toPending();
expect(await x.or(ok(3))).toStrictEqual(ok(2));
expect(await x.or(err("another one"))).toStrictEqual(ok(2));
expect(await y.or(ok(3))).toStrictEqual(ok(3));
expect(await y.or(err("another one"))).toStrictEqual(err("failure"));
expect((await y.or(Promise.reject(new Error("boom")))).unwrapErr().unexpected).toBeDefined();
orElse
Returns this PendingResult
if it resolves to Ok
, otherwise
returns a PendingResult
with the result of f
.
This is the asynchronous version of orElse
.
If f
throws or returns a rejected promise, the resulting PendingResult
resolves
to an Err
with an UnexpectedError
.
const x = ok<number, string>(2).toPending();
const y = err<number, string>("failure").toPending();
expect(await x.orElse(() => ok(3))).toStrictEqual(ok(2));
expect(await y.orElse(() => Promise.resolve(ok(3)))).toStrictEqual(ok(3));
expect((await y.orElse(() => { throw new Error("boom") })).unwrapErr().unexpected).toBeDefined();
expect(await y.orElse(() => err("another one"))).toStrictEqual(err("another one"));
tap
tap(f: (x: Result<T, E>) => unknown): PendingResult<T, E>
Executes f
with the resolved result, then returns a new PendingResult
unchanged.
This is the asynchronous version of tap
.
- If
f
throws or rejects, the error is ignored. - If
f
returns a promise, the promise is not awaited before returning.
const x = pendingResult(ok<number, string>(42));
const y = pendingResult(err<number, string>("failure"));
let log = "";
expect(await x.tap(res => (log = res.toString()))).toStrictEqual(ok(42));
expect(log).toBe("Ok { 42 }");
expect(await y.tap(res => (log = res.toString()))).toStrictEqual(err("failure"));
expect(log).toBe("Err { 'failure' }");
transpose
transpose<U, F>(this: PendingResult<Option<U>, F>): PendingOption<Result<U, F>>
Transposes a PendingResult
of an Option
into a PendingOption
containing a Result
.
This is the asynchronous version of transpose
.
Only available when the PendingResult
resolves to an Option
.
const x = pendingOption(some(ok(2)));
const y = pendingOption(some(err("error")));
const z = pendingOption(none<Result<number, string>>());
expect(await x.transpose()).toStrictEqual(ok(some(2)));
expect(await y.transpose()).toStrictEqual(err("error"));
expect(await z.transpose()).toStrictEqual(ok(none()));
try
try(): Promise<[boolean, CheckedError<Awaited<E>> | undefined, Awaited<T> | undefined]>
Extracts this PendingResult
’s state, returning a promise of a tuple with a success flag, error, and value.
Inspired by the Try Operator
proposal.
This is the asynchronous version of try
.
- Resolves to
[true, undefined, Awaited<T>]
if this is anOk
, or[false, CheckedError<Awaited<E>>, undefined]
if this is anErr
. - Never rejects, offering a safe way to await the result’s state with explicit success indication.
const x = ok<number, string>(42).toPending();
const y = err<number, string>("failure").toPending();
expect(await x.try()).toEqual([true, undefined, 42]);
expect(await y.try()).toEqual([false, expect.objectContaining({ expected: "failure" }), undefined]);