UNPKG

typescript-functional-extensions

Version:

A TypeScript implementation of synchronous and asynchronous Maybe and Result monads

300 lines (299 loc) 9.11 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.Maybe = void 0; const maybeAsync_js_1 = require("./maybeAsync.js"); const result_js_1 = require("./result.js"); const unit_js_1 = require("./unit.js"); const utilities_js_1 = require("./utilities.js"); /** * Represents a value that might not exist. Undefined and null values are always represented as Maybe.none. */ class Maybe { /** * Creates a new Maybe with a value * @param value The value of the new maybe * @returns */ static some(value) { return new Maybe(value); } /** * Creates a new Maybe with no value * @returns {Maybe} */ static none() { return new Maybe(); } /** * Creates a new Maybe. If no value is provided, it is equivalent to calling Maybe.none(), and * if a value is provided, it is equivalent to calling Maybe.some(val) * @param value The value of the new Maybe. * @returns {Maybe} */ static from(value) { return new Maybe(value); } static tryFirst(values, predicate) { if ((0, utilities_js_1.isFunction)(predicate)) { return new Maybe(values.find(predicate)); } else { return new Maybe(values[0]); } } static tryLast(values, predicate) { if ((0, utilities_js_1.isFunction)(predicate)) { for (let index = values.length - 1; index >= 0; index--) { const value = values[index]; if (predicate(value)) { return new Maybe(value); } } return Maybe.none(); } else { return new Maybe(values[values.length - 1]); } } static choose(maybes, projection) { if ((0, utilities_js_1.isFunction)(projection)) { const values = []; for (const m of maybes) { if (m.hasNoValue) { continue; } const original = m.getValueOrThrow(); values.push(projection(original)); } return values; } else { const values = []; for (const m of maybes) { if (m.hasNoValue) { continue; } const original = m.getValueOrThrow(); values.push(original); } return values; } } value; /** * Returns true if the Maybe contains a value */ get hasValue() { return (0, utilities_js_1.isDefined)(this.value); } /** * Returns true if the Maybe has no value */ get hasNoValue() { return !this.hasValue; } constructor(value) { this.value = (0, utilities_js_1.isDefined)(value) ? value : undefined; } getValueOrDefault(defaultValueOrFactory) { if ((0, utilities_js_1.isDefined)(this.value)) { return this.value; } if ((0, utilities_js_1.isFunction)(defaultValueOrFactory)) { return defaultValueOrFactory(); } return defaultValueOrFactory; } /** * Returns the value of the Maybe and throws * and Error if there is none * @returns */ getValueOrThrow() { if ((0, utilities_js_1.isSome)(this.value)) { return this.value; } throw Error('No value'); } /** * Executes the given operator functions, creating a custom pipeline * @param operations Maybe operation functions * @returns */ pipe(...operations) { return (0, utilities_js_1.pipeFromArray)(operations)(this); } /** * Converts the value of the Maybe, if there is one, to a new value * as defined by the provided projection function * @param projection * @returns */ map(projection) { return this.hasValue ? Maybe.some(projection(this.getValueOrThrow())) : Maybe.none(); } /** * Converts the value of the Maybe, if there is one, to a new value * as defined by the provided projection, wrapping the asynchronous result in a MaybeAsync * @param projection * @returns */ mapAsync(projection) { return this.hasValue ? maybeAsync_js_1.MaybeAsync.from(projection(this.getValueOrThrow())) : maybeAsync_js_1.MaybeAsync.none(); } /** * Executes the given action if the Maybe has a value * @param action * @returns */ tap(action) { if (this.hasValue) { action(this.getValueOrThrow()); } return this; } /** * Executes the given asynchronous action if the Maybe has a * value and retursn a new MaybeAsync * @param asyncAction * @returns */ tapAsync(asyncAction) { if (this.hasNoValue) { return maybeAsync_js_1.MaybeAsync.none(); } const promise = new Promise((resolve) => { const value = this.getValueOrThrow(); asyncAction(value).then(() => resolve(value)); }); return maybeAsync_js_1.MaybeAsync.from(promise); } /** * Executes an action if the Maybe has no value * @param action */ tapNone(action) { if (this.hasNoValue) { action(); } return this; } /** * Executes an action if the Maybe has no value * @param action */ tapNoneAsync(action) { if (this.hasValue) { return maybeAsync_js_1.MaybeAsync.some(this.getValueOrThrow()); } return maybeAsync_js_1.MaybeAsync.from(action().then(() => Maybe.none())); } /** * Converts the value of the Maybe, if it has one, to a new Maybe * @param projection * @returns */ bind(projection) { return this.hasValue ? projection(this.getValueOrThrow()) : Maybe.none(); } /** * Converts the value of the Maybe, if it has one, to a new * MaybeAsync * @param projection * @returns */ bindAsync(projection) { return this.hasValue ? projection(this.getValueOrThrow()) : maybeAsync_js_1.MaybeAsync.none(); } match(matcherOrprojection) { if (this.hasValue) { const someResult = matcherOrprojection.some(this.getValueOrThrow()); return (0, utilities_js_1.isDefined)(someResult) ? someResult : unit_js_1.Unit.Instance; } const noneResult = matcherOrprojection.none(); return (0, utilities_js_1.isDefined)(noneResult) ? noneResult : unit_js_1.Unit.Instance; } /** * Executes the given action if the Maybe has a value * @param action */ execute(action) { if (this.hasValue) { action(this.getValueOrThrow()); } return unit_js_1.Unit.Instance; } /** * Executes the given async action if the Maybe has a value * @param action A void Promise returning function * @returns A Promise containing Unit */ executeAsync(action) { if (this.hasValue) { return action(this.getValueOrThrow()).then(() => unit_js_1.Unit.Instance); } return Promise.resolve(unit_js_1.Unit.Instance); } or(fallback) { if (this.hasValue) { return new Maybe(this.getValueOrThrow()); } if ((0, utilities_js_1.isFunction)(fallback)) { const maybeOrValue = fallback(); return maybeOrValue instanceof Maybe ? maybeOrValue : new Maybe(maybeOrValue); } else if (fallback instanceof Maybe) { return fallback; } return new Maybe(fallback); } orAsync(fallback) { if (this.hasValue) { return maybeAsync_js_1.MaybeAsync.some(this.getValueOrThrow()); } if ((0, utilities_js_1.isPromise)(fallback)) { return maybeAsync_js_1.MaybeAsync.from(fallback); } if ((0, utilities_js_1.isFunction)(fallback)) { return maybeAsync_js_1.MaybeAsync.from(fallback()); } return fallback; } /** * Converts the Maybe into a Result. The Result is successful if there is a value * and a failure, with the given error, if there is not * @param error * @returns */ toResult(error) { return this.hasValue ? result_js_1.Result.success(this.getValueOrThrow()) : result_js_1.Result.failure(error); } /** * Returns the string representation of the Maybe (either some or none) * @returns */ toString() { return this.hasValue ? `Maybe.some` : 'Maybe.none'; } /** * Returns true if the Maybes both have values and the values are strictly equal * @param maybe * @returns */ equals(maybe) { return (this.hasValue && maybe.hasValue && this.getValueOrThrow() === maybe.getValueOrThrow()); } } exports.Maybe = Maybe;