PendingOption
PendingOption
represents an
Option
, whose value is not yet resolved (thus, pending). Due to
the asynchronous nature of JavaScript, a single Option
type is
not enough to represent the conditional value, PendingOption
closes this gap and offers
rich set of methods, similar to ones an Option
has.
PendingOption
implements PromiseLike<Option<T>>
interface, which means that
it can be Awaited
as a regular Promise
.
In order to access the value of PendingOption
, you need to await it, which
will resolve it to either Some<T>
or None
variant. Once resolved, you can
use the methods available on Option
to work with the value.
Constructors
pendingNone()
- creates aPendingOption
that resolves toNone
variant.pendingSome<T>(value: T | Promise<T>)
- creates aPendingOption
that resolves toSome<T>
variant.pendingOption(optionOrFactory: Option<T> | Promise<Option<T>> | (() => Option<T> | Promise<Option<T>>))
- creates aPendingOption
that resolves to the providedOption
.
Methods
and
and<U>(x: Option<U> | Promise<Option<U>>): PendingOption<Awaited<U>>
Returns a PendingOption
with None
if this option resolves to None
,
otherwise returns a PendingOption
with x
.
This is the asynchronous version of and
.
If x
is a Promise
and rejects, None
is returned.
const x = pendingOption(some(2));
const y = pendingOption(none<number>());
expect(await x.and(some(3))).toStrictEqual(some(3));
expect(await x.and(Promise.resolve(some(3)))).toStrictEqual(some(3));
expect(await x.and(none())).toStrictEqual(none());
expect(await x.and(Promise.resolve(none()))).toStrictEqual(none());
expect(await y.and(some(3))).toStrictEqual(none());
expect(await y.and(Promise.resolve(none()))).toStrictEqual(none());
andThen
andThen<U>(f: (x: T) => Option<U> | Promise<Option<U>>): PendingOption<Awaited<U>>
Returns a PendingOption
with None
if this Option
resolves to None
,
otherwise applies f
to the resolved value and returns the result.
This is the asynchronous version of andThen
.
If f
rejects or throws, None
is returned.
const x = pendingOption(some(2));
const y = pendingOption(none<number>());
expect(await x.andThen((n) => some(n * 2))).toStrictEqual(some(4));
expect(await x.andThen((n) => Promise.resolve(some(n * 2)))).toStrictEqual(some(4));
expect(await x.andThen((_) => none())).toStrictEqual(none());
expect(await y.andThen((n) => some(n * 2))).toStrictEqual(none());
combine
Combines this PendingOption
with other Option
or PendingOption
instances into a single PendingOption
containing a tuple of resolved values.
The combine
method takes an arbitrary number of Option
or PendingOption
instances. It resolves all inputs and returns a PendingOption
that, when
resolved, contains an Option
with a tuple of their values if all resolve
to Some
. If any input resolves to None
, the result resolves to None
.
The resulting tuple includes the resolved value of this PendingOption
as
the first element, followed by the resolved values from the provided instances.
const a = pendingSome(1);
const b = some(Promise.resolve("hi"));
const c = none<Error>();
const d = pendingNone<Promise<Date>>();
const e = a.combine(b, c, d); // PendingOption<[number, string, Error, Date]>
filter
filter(f: (x: T) => boolean | Promise<boolean>): PendingOption<T>
Returns a PendingOption
with None
if the option resolves to None
,
otherwise calls f
with the resolved value and returns a PendingOption
with the original value if f
resolves to true
, or None
otherwise.
This is the asynchronous version of filter
.
If f
rejects or throws, None
is returned.
const x = pendingOption(some(2));
const y = pendingOption(none<number>());
expect(await x.filter((n) => n > 0)).toStrictEqual(some(2));
expect(await x.filter((n) => Promise.resolve(n < 0))).toStrictEqual(none());
expect(await y.filter((_) => true)).toStrictEqual(none());
flatten
Flattens a PendingOption
of a PendingOption
or Option
, resolving nested pending states.
This is the asynchronous version of flatten
.
If inner Option
is wrapped in a Promise
and rejects,
flattened PendingOption
with None
is returned.
const option1: PendingOption<Option<number>> = getPendingOption();
option1.flatten(); // PendingOption<number>
const option2: PendingOption<PendingOption<number>> = getPendingOption();
option2.flatten(); // PendingOption<number>
const option3: PendingOption<PendingOption<PendingOption<number>>> = getPendingOption();
option3.flatten(); // PendingOption<Option<number>>
inspect
inspect(f: (x: T) => unknown): PendingOption<T>
Calls f
with the resolved value if the option is Some
, then returns this
PendingOption
unchanged. Useful for side effects.
This is the asynchronous version of inspect
.
Returns a new PendingOption
instance with the same value as the original,
rather than the exact same reference. The returned option is a distinct object,
preserving the original value.
const x = pendingOption(some(2));
const y = pendingOption(none<number>());
let sideEffect = 0;
expect(await x.inspect((n) => (sideEffect = n))).toStrictEqual(some(2));
expect(sideEffect).toBe(2);
expect(await y.inspect((n) => (sideEffect = n))).toStrictEqual(none());
expect(sideEffect).toBe(2); // Unchanged
iter
iter(): AsyncIterableIterator<Awaited<T>, Awaited<T>, void>
Returns an async iterator over the pending option’s value, yielding it if
it resolves to Some
or nothing if it resolves to None
.
- Yields exactly one item for a resolved
Some
, or zero items for a resolvedNone
. - Compatible with
for await...of
loops and async spread operators (with caution).
const x = some(42).toPending();
const y = none<number>().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): PendingOption<Awaited<U>>
Maps the resolved value with f
, returning a PendingOption
with the
result if Some
, or None
otherwise.
This is the asynchronous version of map
.
If f
throws or rejects, returns None
.
const x = pendingOption(some(2));
const y = pendingOption(none<number>());
expect(await x.map((n) => n * 2)).toStrictEqual(some(4));
expect(await x.map((n) => Promise.resolve(n * 2))).toStrictEqual(some(4));
expect(await y.map((n) => n * 2)).toStrictEqual(none());
mapAll
mapAll<U>(f: (x: Option<T>) => Option<U> | Promise<Option<U>>): PendingOption<Awaited<U>>
Maps this option by applying a callback to its full state, executing the
callback for both Some
and None
, returning a new PendingOption
.
Unlike andThen
, which only invokes the callback for Some
,
this method always calls f
, passing the entire Option
as its argument.
This is the asynchronous version of mapAll
.
If f
throws or returns a Promise
that rejects, the newly
created PendingOption
will resolve to a None
.
const someOpt = pendingOption(some(42));
const noneOpt = pendingOption(none<number>());
const someMapped = someOpt.mapAll((opt) => Promise.resolve(some(opt.unwrapOr(0))));
expect(await someMapped).toStrictEqual(some(42));
const noneMapped = noneOpt.mapAll((opt) => Promise.resolve(some(opt.unwrapOr(0) + 1)));
expect(await noneMapped).toStrictEqual(some(1));
match
match<U, F = U>(f: (x: T) => U, g: () => F): Promise<Awaited<U | F>>
Matches the resolved option, returning f
applied to the value if Some
,
or g
if None
. Returns a Promise
with the result.
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 OptionError
if f
or g
throws an exception or rejects,
original error will be set as OptionError.reason
.
const x = pendingOption(some(2));
const y = pendingOption(none<number>());
expect(await x.match(n => n * 2, () => 0)).toBe(4);
expect(await y.match(n => n * 2, () => 0)).toBe(0);
await expect(y.match(n => n * 2, () => { throw new Error() })).rejects.toThrow(OptionError);
okOr
okOr<E>(y: Awaited<E>): PendingResult<T, E>
Converts to a PendingResult
, using y
as the error value if the
PendingOption
resolves to None
.
This is the asynchronous version of okOr
.
const x = pendingOption(some(2));
const y = pendingOption(none<number>());
expect(await x.okOr("error")).toStrictEqual(ok(2));
expect(await y.okOr("error")).toStrictEqual(err("error"));
okOrElse
okOrElse<E>(mkErr: () => E | Promise<E>): PendingResult<T, E>
Converts to a PendingResult
, using the result of mkErr
as the error value if this resolves to None
.
This is the asynchronous version of okOrElse
.
const x = pendingOption(some(2));
const y = pendingOption(none<number>());
expect(await x.okOrElse(() => "error")).toStrictEqual(ok(2));
expect(await y.okOrElse(() => Promise.resolve("error"))).toStrictEqual(err("error"));
or
or(x: Option<T> | Promise<Option<T>>): PendingOption<Awaited<T>>
Returns this PendingOption
if it resolves to Some
, otherwise
returns a PendingOption
with x
.
This is the asynchronous version of or
.
If x
is a Promise
that rejects, None
is returned.
const x = pendingOption(some(2));
const y = pendingOption(none<number>());
expect(await x.or(some(3))).toStrictEqual(some(2));
expect(await x.or(Promise.resolve(none()))).toStrictEqual(some(2));
expect(await y.or(some(3))).toStrictEqual(some(3));
expect(await y.or(Promise.resolve(none()))).toStrictEqual(none());
orElse
orElse(f: () => Option<T> | Promise<Option<T>>): PendingOption<Awaited<T>>
Returns this PendingOption
if it resolves to Some
, otherwise
returns a PendingOption
with the result of f
.
This is the asynchronous version of orElse
.
If f
throws or rejects, None
is returned.
const x = pendingOption(some(2));
const y = pendingOption(none<number>());
expect(await x.orElse(() => some(3))).toStrictEqual(some(2));
expect(await y.orElse(() => some(1))).toStrictEqual(some(1));
expect(await y.orElse(() => Promise.resolve(some(3)))).toStrictEqual(some(3));
tap
tap(f: (x: Option<T>) => unknown): PendingOption<T>
Executes f
with the resolved option, then returns a new PendingOption
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 = pendingOption(some(42));
const y = pendingOption(none<number>());
let log = "";
expect(await x.tap((opt) => (log = opt.toString()))).toStrictEqual(some(42));
expect(log).toBe("Some { 42 }");
expect(await y.tap((opt) => (log = opt.toString()))).toStrictEqual(none());
expect(log).toBe("None");
transpose
transpose<U, E>(this: PendingOption<Result<U, E>>): PendingResult<Option<U>, E>
Transposes a PendingOption
of a Result
into a PendingResult
containing an Option
.
This is the asynchronous version of transpose
.
Only available on PendingOption
s that hold a Result
value.
const x = pendingOption(none<Result<number, string>>());
const y = pendingOption(some<Result<number, string>>(ok(2)));
const z = pendingOption(some<Result<number, string>>(err("error")));
expect(await x.transpose()).toStrictEqual(ok(none()));
expect(await y.transpose()).toStrictEqual(ok(some(2)));
expect(await z.transpose()).toStrictEqual(err("error"));
xor
xor(y: Option<T> | Promise<Option<T>>): PendingOption<Awaited<T>>
Returns a PendingOption
with Some
if exactly one of this option or
y
resolves to Some
, otherwise returns a PendingOption
with None
.
This is the asynchronous version of xor
.
If y
is a Promise
that rejects, None
is returned.
const x = pendingOption(some(2));
const y = pendingOption(none<number>());
expect(await x.xor(some(3))).toStrictEqual(none());
expect(await x.xor(Promise.resolve(none()))).toStrictEqual(some(2));
expect(await y.xor(some(3))).toStrictEqual(some(3));
expect(await y.xor(Promise.resolve(none()))).toStrictEqual(none());