@cprussin/option-result
Version:
Yet another Typescript clone of the rust `Option` & `Result` enums.
498 lines (497 loc) • 17.9 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Err = exports.Ok = exports.Result = void 0;
const option_js_1 = require("./option.js");
const _Ok = (value) => ({ isOk: true, value });
const _Err = (error) => ({ isOk: false, error });
/**
* A type which represents values that may encode either a successful result or
* an error result.
*
* @typeParam T - the type of successful results
* @typeParam E - the type of error results
*/
class Result {
/** @hidden */
data;
/** @hidden */
constructor(data) {
this.data = data;
}
/**
* Construct a `Result` containing a success value.
*
* @category Constructing
* @typeParam T - the type of the value
* @typeParam E - the type of error results
* @param value - the value contained by the `Result`
* @returns a `Result` containing `value`
* @see {@link Err}
* @see {@link wrap}
* @see {@link wrapAsync}
*/
static Ok(value) {
return new Result(_Ok(value));
}
/**
* Construct a `Result` containing an error value.
*
* @category Constructing
* @typeParam T - the type of success values
* @typeParam E - the type of the error value
* @param error - the error contained by the `Result`
* @returns a `Result` containing an error `error`
* @see {@link Ok}
* @see {@link wrap}
* @see {@link wrapAsync}
*/
static Err(error) {
return new Result(_Err(error));
}
/**
* Takes an Array of {@link Result} values. If any of the {@link Result}
* values in the array is `Err`, returns that `Err`. Otherwise, returns `Ok`
* containing an Array of each value inside each {@link Result} in the
* original list.
*
* @category Transforming contained values
* @typeParam T - the type contained by the {@link Result} values
* @typeParam E - the error type of the {@link Result} values
* @param results - the {@link Result} values to collect
* @returns `Ok` containing an Array of values contained by each {@link
* Result} in the original Array if all {@link Result} values in the original
* list are `Ok`, otherwise returns the first `Err` in the Array
* @see {@link map}
* @see {@link mapAsync}
* @see {@link mapOr}
* @see {@link mapErr}
* @see {@link mapErrAsync}
*/
static collect(results) {
const values = [];
for (const result of results) {
if (result.data.isOk) {
values.push(result.data.value);
}
else {
return Result.Err(result.data.error);
}
}
return (0, exports.Ok)(values);
}
/**
* Takes a function that could be throw and converts it into an {@link
* Result}.
*
* @category Constructing
* @typeParam T - the type contained by the {@link Result}
* @typeParam E - the error type of the {@link Result}
* @param fn - the function which could throw
* @returns `Ok` with the return value of `fn` if `fn` doesn't throw,
* otherwise `Err` containing an {@link Option} which is `None` if the
* exception is `null` or `undefined`, and is `Some` with the exception
* otherwise
* @see {@link Ok}
* @see {@link Err}
* @see {@link wrapAsync}
*/
static wrap(fn) {
try {
return Result.Ok(fn());
}
catch (error) {
return Result.Err(option_js_1.Option.wrap(error));
}
}
/**
* Takes a promise that could reject and converts it into an {@link Result}.
*
* @category Constructing
* @typeParam T - the type contained by the {@link Result}
* @typeParam E - the error type of the {@link Result}
* @param promise - the promise to convert
* @returns a Promise containing `Ok` with the value resolved by `promise` if
* `promise` doesn't reject, otherwise `Err` containing an {@link Option}
* which is `None` if `promise` rejects with a `null` or `undefined`, and is
* `Some` with the rejection value otherwise
* @see {@link Ok}
* @see {@link Err}
* @see {@link wrap}
*/
static async wrapAsync(promise) {
try {
return Result.Ok(await promise);
}
catch (error) {
return Result.Err(option_js_1.Option.wrap(error));
}
}
/**
* Check if `this` is `Ok`.
*
* @category Querying the variant
* @returns true if `this` is `Ok`, `false` otherwise
* @see {@link isErr}
*/
isOk() {
return this.data.isOk;
}
/**
* Check if `this` is `Err`.
*
* @category Querying the variant
* @returns true if `this` is `Err`, `false` otherwise
* @see {@link isOk}
*/
isErr() {
return !this.data.isOk;
}
/**
* Takes two functions, one is called with the contained value if `this` is
* `Ok` and the other is called with the error if `this` is `Err`.
*
* @category Extracting the contained value
* @typeParam U - the type of the return value of the matcher functions
* @param matchers - an object containing matcher functions
* @returns the return value of the `Ok` or `Err` matcher function
* @see {@link unwrapOrElse}
* @see {@link unwrapOr}
*/
match(matchers) {
return this.data.isOk
? matchers.Ok(this.data.value)
: matchers.Err(this.data.error);
}
/**
* Returns the contained value if `this` is `Ok`, otherwise call the provided
* function with the error value and return the result.
*
* @category Extracting the contained value
* @param defaultValue - a function that will be called on the error value if
* `this` is `Err`
* @returns the contained if `this` is `Ok`, otherwise the return value from
* calling `defaultValue` on the error value
* @see {@link unwrapOr}
* @see {@link match}
*/
unwrapOrElse(defaultValue) {
return this.match({
Ok: (value) => value,
Err: defaultValue,
});
}
/**
* Returns the contained value if `this` is `Ok`, otherwise return the
* provided default value, ignoring any error value.
*
* @category Extracting the contained value
* @param defaultValue - the value to return if `this` is `Err`
* @returns the contained if `this` is `Ok`, otherwise `defaultValue`
* @see {@link unwrapOrElse}
* @see {@link match}
*/
unwrapOr(defaultValue) {
return this.unwrapOrElse(() => defaultValue);
}
/**
* Takes a function which takes the value contained in `this` and returns a
* new {@link Result}; calls it and returns the result if `this` is `Ok`,
* otherwise returns `this`. Also sometimes known in other languages or
* libraries as `flatmap` or `bind`.
*
* @category Boolean operators
* @typeParam U - the type contained in the {@link Result} returned by `fn`
* @param fn - a function that will be called with the value in `this` if
* `this` is `Ok`
* @returns the result of calling `fn` if `this` is `Ok`, `this` otherwise
* @see {@link and}
* @see {@link andThenAsync}
* @see {@link or}
* @see {@link orElse}
* @see {@link orElseAsync}
*/
andThen(fn) {
return this.match({
Ok: fn,
Err: (error) => Result.Err(error),
});
}
/**
* Takes another {@link Result} and returns it if `this` is `Ok`, otherwise
* returns `this`.
*
* @category Boolean operators
* @typeParam U - the type contained in the {@link Result} returned by `fn`
* @param other - a {@link Result} to return if `this` is `Ok`
* @returns `other` if `this` is `Ok`, `this` otherwise
* @see {@link andThen}
* @see {@link andThenAsync}
* @see {@link or}
* @see {@link orElse}
* @see {@link orElseAsync}
*/
and(other) {
return this.andThen(() => other);
}
/**
* Takes a function which takes the value contained in `this` and returns a
* promise which resolves to a new {@link Result}; calls it and returns the
* result if `this` is `Ok`, otherwise returns a promise which resolves to
* `this`. This is an async version of {@link andThen}.
*
* @category Boolean operators
* @typeParam U - the type contained in the {@link Result} returned by `fn`
* @param fn - a function that will be called with the value in `this` if
* `this` is `Ok`
* @returns the result of calling `fn` if `this` is `Ok`, `this` otherwise
* @see {@link and}
* @see {@link andThen}
* @see {@link or}
* @see {@link orElse}
* @see {@link orElseAsync}
*/
andThenAsync(fn) {
return this.match({
Ok: fn,
Err: (error) => Promise.resolve(Result.Err(error)),
});
}
/**
* Takes a function which takes an error and returns a {@link Result}; calls
* it on the contained error and returns the result if `this` is `Err`,
* otherwise returns `this`.
*
* @category Boolean operators
* @param fn - a function that will be called with the error value if `this`
* is `Err`
* @returns the result of calling `fn` if `this` is `Err`, `this` otherwise
* @see {@link and}
* @see {@link andThen}
* @see {@link andThenAsync}
* @see {@link or}
* @see {@link orElseAsync}
*/
orElse(fn) {
return this.match({
Ok: (value) => Result.Ok(value),
Err: fn,
});
}
/**
* Takes another {@link Result} and returns it if `this` is `Err`, otherwise
* returns `this`.
*
* @category Boolean operators
* @param other - a {@link Result} to return if `this` is `Err`
* @returns `other` if `this` is `Err`, `this` otherwise
* @see {@link and}
* @see {@link andThen}
* @see {@link andThenAsync}
* @see {@link orElse}
* @see {@link orElseAsync}
*/
or(other) {
return this.orElse(() => other);
}
/**
* Takes a function which takes an error and returns a promise which resolves
* to a new {@link Result}; calls it with the contained error and returns the
* result if `this` is `Err`, otherwise returns a promise which resolves to
* `this`.
*
* @category Boolean operators
* @param fn - a function that will be called if `this` is `Err`
* @returns the result of calling `fn` if `this` is `Err`, `this` otherwise
* @see {@link and}
* @see {@link andThen}
* @see {@link andThenAsync}
* @see {@link or}
* @see {@link orElse}
*/
orElseAsync(fn) {
return this.match({
Ok: (value) => Promise.resolve(Result.Ok(value)),
Err: fn,
});
}
/**
* Transforms `Result<T, E>` to `Result<U, E>` by applying the provided
* function to the contained value of `Ok` and leaving `Err` values unchanged.
*
* @category Transforming contained values
* @typeParam U - the type of the return value of `fn`
* @param fn - the function to apply to the value contained if `this` is `Ok`
* @returns `this` if `this` is `Err`, otherwise `Ok` containing the result of
* applying `fn` to the value in `this`
* @see {@link mapAsync}
* @see {@link mapOr}
* @see {@link mapErr}
* @see {@link mapErrAsync}
* @see {@link collect}
*/
map(fn) {
return this.andThen((value) => (0, exports.Ok)(fn(value)));
}
/**
* Transforms `Result<T, E>` to `Promise<Result<U, E>>` by applying the
* provided async function to the contained value of `Ok` and resolving `Err`
* values unchanged.
*
* @category Transforming contained values
* @typeParam U - the type of the value in the promise returned by `fn`
* @param fn - the function to apply to the value contained if `this` is `Ok`
* @returns a promise resolving to `this` if `this` is `Err`, otherwise a
* promise resolving to `Ok` containing the value resolved by the promise
* returned from applying `fn` to the value in `this`
* @see {@link map}
* @see {@link mapOr}
* @see {@link mapErr}
* @see {@link mapErrAsync}
* @see {@link collect}
*/
mapAsync(fn) {
return this.andThenAsync(async (value) => (0, exports.Ok)(await fn(value)));
}
/**
* Applies the provided function to the contained value if `this` is `Ok`,
* otherwise returns the provided default value.
*
* @category Transforming contained values
* @typeParam U - the type of `defaultValue` and the value returned by `fn`
* @param defaultValue - the value to return if `this` is `Err`
* @param fn - the function to apply to the value contained if `this` is `Ok`
* @returns the result of applying `fn` to the value in `this` if `this` is
* `Ok`, otherwise `defaultValue`
* @see {@link map}
* @see {@link mapAsync}
* @see {@link mapErr}
* @see {@link mapErrAsync}
* @see {@link collect}
*/
mapOr(defaultValue, fn) {
return this.map(fn).unwrapOr(defaultValue);
}
/**
* Transforms `Result<T, E>` to `Result<T, F>` by applying the provided
* function to the contained value of `Err` and leaving `Ok` values unchanged.
*
* @category Transforming contained values
* @typeParam F - the type of the return value of `fn`
* @param fn - the function to apply to the value contained if `this` is `Err`
* @returns `this` if `this` is `Ok`, otherwise `Err` containing the result of
* applying `fn` to the value in `this`
* @see {@link map}
* @see {@link mapAsync}
* @see {@link mapOr}
* @see {@link mapErrAsync}
* @see {@link collect}
*/
mapErr(fn) {
return this.match({
Ok: (value) => Result.Ok(value),
Err: (error) => Result.Err(fn(error)),
});
}
/**
* Transforms `Result<T, E>` to `Promise<Result<T, F>>` by applying the
* provided async function to the contained value of `Err` and resolving `Ok`
* values unchanged.
*
* @category Transforming contained values
* @typeParam F - the type of the value in the promise returned by `fn`
* @param fn - the function to apply to the value contained if `this` is `Err`
* @returns a promise resolving to `this` if `this` is `Ok`, otherwise a
* promise resolving to `Err` containing the value resolved by the promise
* returned from applying `fn` to the value in `this`
* @see {@link map}
* @see {@link mapAsync}
* @see {@link mapOr}
* @see {@link mapErr}
* @see {@link collect}
*/
mapErrAsync(fn) {
return this.match({
Ok: (value) => Promise.resolve(Result.Ok(value)),
Err: async (error) => Result.Err(await fn(error)),
});
}
/**
* If `this` is `Ok`, returns {@link Option.Some} containing the value in
* `this`. Otherwise return {@link Option.None}.
*
* @category Interacting with Option
* @returns {@link Option.Some} with the value in `this` if `this` is `Ok`,
* {@link Option.None} otherwise
* @see {@link err}
* @see {@link transpose}
*/
ok() {
return this.match({
Ok: (value) => (0, option_js_1.Some)(value),
Err: () => (0, option_js_1.None)(),
});
}
/**
* If `this` is `Err`, returns {@link Option.Some} containing the value in
* `this`. Otherwise return {@link Option.None}.
*
* @category Interacting with Option
* @returns {@link Option.Some} with the value in `this` if `this` is `Err`,
* {@link Option.None} otherwise
* @see {@link ok}
* @see {@link transpose}
*/
err() {
return this.match({
Ok: () => (0, option_js_1.None)(),
Err: (error) => (0, option_js_1.Some)(error),
});
}
/**
* Convert a {@link Result} containing an {@link Option} into an {@link
* Option} containing a {@link Result}.
*
* @category Interacting with Option
* @typeParam T - the type of the value contained in the {@link Option}
* contained in `this`
* @returns if `this` is `Err`, returns {@link Option.Some} containing `this`;
* if `this` is `Ok` and the {@link Option} in `this` is {@link Option.None},
* returns {@link Option.None}; otherwise returns {@link Option.Some}
* containing `Ok` containing the value in the {@link Option} in `this`
* @see {@link ok}
* @see {@link err}
*/
transpose() {
return this.match({
Ok: (option) => option.map((value) => Result.Ok(value)),
Err: (error) => (0, option_js_1.Some)(Result.Err(error)),
});
}
}
exports.Result = Result;
/**
* Construct a {@link Result} containing a success value.
*
* @typeParam T - the type of the value
* @typeParam E - the type of error results
* @param value - the value contained by the {@link Result}
* @returns a {@link Result} containing `value`
* @see {@link Result.Err}
* @see {@link Result.wrap}
* @see {@link Result.wrapAsync}
*/
const Ok = (value) => Result.Ok(value);
exports.Ok = Ok;
/**
* Construct a {@link Result} containing an error value.
*
* @typeParam T - the type of success values
* @typeParam E - the type of the error value
* @param error - the error contained by the {@link Result}
* @returns a {@link Result} containing an error `error`
* @see {@link Result.Ok}
* @see {@link Result.wrap}
* @see {@link Result.wrapAsync}
*/
const Err = (error) => Result.Err(error);
exports.Err = Err;