UNPKG

typescript-monads

Version:
887 lines 34.9 kB
var __extends = (this && this.__extends) || (function () { var extendStatics = function (d, b) { extendStatics = Object.setPrototypeOf || ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; }; return extendStatics(d, b); }; return function (d, b) { if (typeof b !== "function" && b !== null) throw new TypeError("Class extends value " + String(b) + " is not a constructor or null"); extendStatics(d, b); function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; })(); var __values = (this && this.__values) || function(o) { var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0; if (m) return m.call(o); if (o && typeof o.length === "number") return { next: function () { if (o && i >= o.length) o = void 0; return { value: o && o[i++], done: !o }; } }; throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined."); }; import { maybe, none } from '../maybe/maybe.factory'; var Result = /** @class */ (function () { function Result() { } Result.ok = function (value) { return new OkResult(value); }; Result.fail = function (value) { return new FailResult(value); }; /** * Creates a Result from a Promise. * * Creates a Promise that will resolve to a Result that is: * - Ok containing the resolved value if the promise resolves successfully * - Fail containing the rejection reason if the promise rejects * * This static method makes it easy to convert Promise-based APIs to Result-based * error handling, giving you more explicit control flow and type safety. * * @param promise The promise to convert to a Result * @returns A Promise that resolves to a Result containing either the value or error * * @example * // Convert a promise to a Result * const resultPromise = Result.fromPromise(fetchData()); * * resultPromise.then(result => { * result.match({ * ok: data => renderData(data), * fail: error => showError(error) * }); * }); * * // With Promise chaining * Result.fromPromise(fetchData()) * .then(result => { * if (result.isOk()) { * const data = result.unwrap(); * return renderData(data); * } else { * const error = result.unwrapFail(); * return showError(error); * } * }); */ Result.fromPromise = function (promise) { return promise .then(function (value) { return new OkResult(value); }) .catch(function (error) { return new FailResult(error); }); }; /** * Creates a Result from an Observable. * * Creates a Promise that will resolve to a Result that is: * - Ok containing the first emitted value if the observable emits a value * - Fail containing the provided error if the observable completes without emitting * - Fail containing the error if the observable errors * * This static method bridges the reactive Observable world with the Result monad, * providing a way to handle emissions, completion, and errors in a functional style. * * Note that this transformation changes the timing model from continuous/reactive * to a one-time asynchronous result. Only the first emission is captured, and the * observable is no longer reactive after transformation. * * @param observable The observable to convert to a Result * @param defaultError The error to use if the observable completes without emitting * @returns A Promise that resolves to a Result containing either the value or error * * @requires rxjs@^7.0 * @example * // Convert an observable to a Result * Result.fromObservable( * userService.getUser(userId), * new Error('User not found') * ).then(result => { * result.match({ * ok: user => renderUser(user), * fail: error => showUserNotFound(error) * }); * }); */ Result.fromObservable = function (observable, defaultError) { return import('rxjs').then(function (_a) { var firstValueFrom = _a.firstValueFrom, take = _a.take, map = _a.map, catchError = _a.catchError; return firstValueFrom(observable.pipe(take(1), map(function (value) { return new OkResult(value); }), catchError(function (error) { // Return the error from the observable directly rather than using EMPTY return [new FailResult(error)]; }))).then(function (result) { return result; }, function () { return new FailResult(defaultError); } // Handle the case where firstValueFrom rejects ); }); }; /** * Transforms an array of Result values into a Result containing an array of values. * * If all Results in the input array are Ok, returns an Ok Result containing an array of all values. * If any Result in the input array is Fail, returns a Fail Result containing the first error encountered. * * This is similar to how Promise.all works but for Results, allowing you to combine * multiple Results and propagate errors if any occur. * * @param results An array of Result values * @returns A Result containing either an array of all Ok values or the first Fail error * * @example * // All Results are Ok * const result1 = Result.sequence([ * ok(1), * ok(2), * ok(3) * ]); * // result1 is Ok([1, 2, 3]) * * // One Result is Fail * const result2 = Result.sequence([ * ok(1), * fail(new Error('Failed')), * ok(3) * ]); * // result2 is Fail(Error('Failed')) */ Result.sequence = function (results) { var e_1, _a; if (results.length === 0) { return new OkResult([]); } var values = []; try { for (var results_1 = __values(results), results_1_1 = results_1.next(); !results_1_1.done; results_1_1 = results_1.next()) { var r = results_1_1.value; if (r.isFail()) { return new FailResult(r.unwrapFail()); } values.push(r.unwrap()); } } catch (e_1_1) { e_1 = { error: e_1_1 }; } finally { try { if (results_1_1 && !results_1_1.done && (_a = results_1.return)) _a.call(results_1); } finally { if (e_1) throw e_1.error; } } return new OkResult(values); }; /** * Alias for sequence, transforms an array of Result values into a Result containing an array of values. * * If all Results in the input array are Ok, returns an Ok Result containing an array of all values. * If any Result in the input array is Fail, returns a Fail Result containing the first error encountered. * * Named to align with Promise.all for familiarity. * * @param results An array of Result values * @returns A Result containing either an array of all Ok values or the first Fail error * * @example * // All Results are Ok * const result1 = Result.all([ * ok(1), * ok(2), * ok(3) * ]); * // result1 is Ok([1, 2, 3]) * * // One Result is Fail * const result2 = Result.all([ * ok(1), * fail(new Error('Failed')), * ok(3) * ]); * // result2 is Fail(Error('Failed')) */ Result.all = function (results) { return Result.sequence(results); }; return Result; }()); export { Result }; var OkResult = /** @class */ (function (_super) { __extends(OkResult, _super); function OkResult(successValue) { var _this = _super.call(this) || this; _this.successValue = successValue; return _this; } /** * Returns true as this is an Ok Result variant. * * This implementation satisfies the abstract method from the Result class * and serves as a TypeScript type guard, allowing TypeScript to narrow the * type to OkResult when `isOk()` returns true. * * @returns true (always, for OkResult instances) */ OkResult.prototype.isOk = function () { return true; }; /** * Returns false as this is not a Fail Result variant. * * This implementation satisfies the abstract method from the Result class * and serves as a TypeScript type guard that will never narrow the type * to FailResult for an OkResult instance. * * @returns false (always, for OkResult instances) */ OkResult.prototype.isFail = function () { return false; }; /** * Extracts the contained Ok value. * * Since this is an OkResult, this method safely returns the contained value * without any risk of exceptions. * * @returns The contained Ok value */ OkResult.prototype.unwrap = function () { return this.successValue; }; /** * Extracts the contained Ok value or returns a default. * * Since this is an OkResult, this method simply returns the contained * value and ignores the default value parameter. * * @param opt The default value (not used in OkResult implementation) * @returns The contained Ok value */ OkResult.prototype.unwrapOr = function () { return this.unwrap(); }; /** * Attempts to extract the contained Fail value, but throws since this is an OkResult. * * This method is unsafe to call on an OkResult and will always throw an exception. * * @throws ReferenceError Always throws with message 'Cannot unwrap a success as a failure' * @returns Never returns a value, always throws */ OkResult.prototype.unwrapFail = function () { throw new ReferenceError('Cannot unwrap a success as a failure'); }; /** * Converts this Ok Result to a Some Maybe containing the success value. * * This method transforms the Ok Result into a Maybe, focusing on the success path. * It always returns a Some Maybe for OkResult instances. * * @returns A Some Maybe containing the non-nullable success value */ OkResult.prototype.maybeOk = function () { return maybe(this.successValue); }; /** * Converts this Ok Result to a None Maybe. * * This method transforms the Ok Result into a Maybe, focusing on the failure path. * Since this is an OkResult with no error value, it always returns a None Maybe. * * @returns A None Maybe (always, for OkResult instances) */ OkResult.prototype.maybeFail = function () { return none(); }; /** * Applies the success branch of a pattern matching object to this Ok Result. * * This method provides a functional way to handle the Ok variant specifically. * For OkResult instances, only the 'ok' function of the pattern is called. * * @typeParam M - The return type of the pattern matching functions * @param fn - An object containing functions to handle each Result variant * @returns The result of applying the 'ok' function to the contained value */ OkResult.prototype.match = function (fn) { return fn.ok(this.successValue); }; /** * Maps the Ok value using the provided function. * * For OkResult instances, this transforms the contained value using the provided * function and returns a new OkResult with the transformed value. * * @typeParam M - The type of the mapped value * @param fn - A function that transforms the Ok value * @returns A new OkResult with the transformed value */ OkResult.prototype.map = function (fn) { return Result.ok(fn(this.successValue)); }; /** * Maps the Fail value using the provided function, which is a no-op for OkResult. * * For OkResult instances, this method ignores the mapping function since there is * no error value to transform. It returns a new OkResult with the same success value * but with the new error type parameter. * * @typeParam M - The type of the mapped error (only changes the type parameter) * @param fn - A function that would transform the Fail value (not used in OkResult) * @returns A new OkResult with the same success value and updated error type */ OkResult.prototype.mapFail = function () { return Result.ok(this.successValue); }; /** * Chains a function that returns another Result, applying it to the contained value. * * This is the monadic bind operation for Result. For OkResult instances, it applies * the function to the contained value and returns the resulting Result directly. * * @typeParam M - The type of the value in the returned Result * @param fn - A function that takes the Ok value and returns a new Result * @returns The Result returned by applying the function to the contained value */ OkResult.prototype.flatMap = function (fn) { return fn(this.successValue); }; /** * Maps the success value to a Maybe, and flattens the resulting structure. * * Since this is an Ok Result, the function is applied to the contained value. * The result depends on whether the Maybe is Some or None: * - If Some: Returns an Ok Result with the unwrapped value * - If None: Returns a Fail Result with the provided error * * This implementation follows the monadic bind operation pattern where we: * 1. Apply the function to get a Maybe * 2. Match on the Maybe to convert it back to a Result * * @param fn Function mapping the contained value to a Maybe * @param err Error value to use if the Maybe is None * @returns Either an Ok Result with the unwrapped value or a Fail Result with the provided error */ OkResult.prototype.flatMapMaybe = function (fn, err) { return fn(this.successValue).match({ some: function (val) { return Result.ok(val); }, none: function () { return Result.fail(err); } }); }; /** * Converts this Ok Result into a Fail Result using a transformation function. * * For OkResult instances, this method applies the function to the contained value * to generate an error and returns a new Fail Result with that error. * * @param fn - A function that transforms the Ok value into a Fail value * @returns A Fail Result with the error generated from the contained value */ OkResult.prototype.toFailWhenOk = function (fn) { return Result.fail(fn(this.successValue)); }; /** * Converts this Ok Result into a Fail Result using a provided error value. * * For OkResult instances, this method ignores the contained value and returns * a new Fail Result with the provided error value. * * @param val - The error value to use in the returned Fail Result * @returns A Fail Result containing the provided error */ OkResult.prototype.toFailWhenOkFrom = function (val) { return Result.fail(val); }; /** * Executes a side-effect function for this Ok Result. * * For OkResult instances, this method calls the 'ok' function if provided. * The 'fail' function is never called, even if provided. * * @param val - An object containing optional functions for Ok and Fail variants */ OkResult.prototype.tap = function (val) { typeof val.ok === 'function' && val.ok(this.successValue); }; /** * Executes a side-effect function with the contained Ok value. * * For OkResult instances, this method always calls the provided function * with the contained value. * * @param fn - A function to execute with the Ok value */ OkResult.prototype.tapOk = function (fn) { fn(this.successValue); }; /** * No-op method for consistency with the Result interface. * * For OkResult instances, this method does nothing since there is no error * value to operate on. */ OkResult.prototype.tapFail = function () { }; /** * No-op method that returns this Result for chaining. * * For OkResult instances, this method simply returns the current Result * without calling any function since there is no error value to operate on. * * @returns This Result unchanged */ OkResult.prototype.tapFailThru = function () { return this; }; /** * Executes a side-effect function with the Ok value and returns this Result for chaining. * * For OkResult instances, this method calls the provided function with the * contained value and then returns the original Result unchanged. * * @param fn - A function to execute with the Ok value * @returns This Result unchanged */ OkResult.prototype.tapOkThru = function (fn) { this.tapOk(fn); return this; }; /** * Executes a side-effect function and returns this Result for chaining. * * For OkResult instances, this method calls the 'ok' function if provided * and then returns the original Result unchanged. * * @param val - An object containing optional functions for Ok and Fail variants * @returns This Result unchanged */ OkResult.prototype.tapThru = function (val) { this.tap(val); return this; }; /** * No-op for OkResult, returns this Result unchanged. * * Since this is already an OkResult, there's no need to recover from an error. * This method simply returns the current Result. * * @returns This Result unchanged */ OkResult.prototype.recover = function () { return this; }; /** * No-op for OkResult, returns this Result unchanged. * * Since this is already an OkResult, there's no need to recover with another Result. * This method simply returns the current Result. * * @returns This Result unchanged */ OkResult.prototype.recoverWith = function () { return this; }; /** * Returns this Result since it's already an Ok variant. * * For OkResult instances, this method ignores the fallback Result and * returns the current Result unchanged. * * @returns This Result unchanged */ OkResult.prototype.orElse = function () { return this; }; /** * Swaps the Ok and Fail values, creating a new Fail Result. * * For OkResult instances, this method transforms the contained success value * into an error and returns a new Fail Result with that error. * * @returns A new Fail Result with the success value as an error */ OkResult.prototype.swap = function () { return Result.fail(this.successValue); }; /** * Combines this Result with another Result using a combining function. * * For OkResult instances, this method applies the function to both values * if the other Result is also Ok, or returns the other Result if it's a Fail. * * @param other - Another Result to combine with this one * @param fn - A function that combines the two Ok values * @returns A new Result containing either the combined values or the error from the other Result */ OkResult.prototype.zipWith = function (other, fn) { var _this = this; return other.flatMap(function (otherValue) { return Result.ok(fn(_this.successValue, otherValue)); }); }; /** * Maps the Ok value of this Result to a Promise, and then flattens the resulting structure. * * For OkResult instances, this method applies the function to the contained value, * awaits the Promise, and wraps the resolved value in a new Ok Result. * If the Promise rejects, it returns a Fail Result with the rejection reason. * * @param fn - A function that takes the Ok value and returns a Promise * @returns A Promise that resolves to a new Result */ OkResult.prototype.flatMapPromise = function (fn) { return fn(this.successValue) .then(function (value) { return Result.ok(value); }) .catch(function (error) { return Result.fail(error); }); }; /** * Maps the Ok value of this Result to an Observable, and then flattens the resulting structure. * * For OkResult instances, this method applies the function to the contained value, * subscribes to the Observable, and wraps the first emitted value in a new Ok Result. * If the Observable errors, it returns a Fail Result with the error. * If the Observable completes without emitting, it returns a Fail Result with the provided default error. * * @param fn - A function that takes the Ok value and returns an Observable * @param defaultError - The error to use if the Observable completes without emitting * @returns A Promise that resolves to a new Result * * @requires rxjs@^7.0 */ OkResult.prototype.flatMapObservable = function (fn, defaultError) { var _this = this; return import('rxjs').then(function (_a) { var firstValueFrom = _a.firstValueFrom, take = _a.take, map = _a.map, catchError = _a.catchError; return firstValueFrom(fn(_this.successValue).pipe(take(1), map(function (value) { return Result.ok(value); }), catchError(function (error) { // Return the error from the observable directly rather than using EMPTY return [Result.fail(error)]; }))).then(function (result) { return result; }, function () { return Result.fail(defaultError); } // Handle the case where firstValueFrom rejects ); }); }; return OkResult; }(Result)); export { OkResult }; var FailResult = /** @class */ (function (_super) { __extends(FailResult, _super); function FailResult(failureValue) { var _this = _super.call(this) || this; _this.failureValue = failureValue; return _this; } /** * Returns false as this is not an Ok Result variant. * * This implementation satisfies the abstract method from the Result class * and serves as a TypeScript type guard that will never narrow the type * to OkResult for a FailResult instance. * * @returns false (always, for FailResult instances) */ FailResult.prototype.isOk = function () { return false; }; /** * Returns true as this is a Fail Result variant. * * This implementation satisfies the abstract method from the Result class * and serves as a TypeScript type guard, allowing TypeScript to narrow the * type to FailResult when `isFail()` returns true. * * @returns true (always, for FailResult instances) */ FailResult.prototype.isFail = function () { return true; }; /** * Attempts to extract the contained Ok value, but throws since this is a FailResult. * * This method is unsafe to call on a FailResult and will always throw an exception. * * @throws Error Always throws with message 'Cannot unwrap a failure' * @returns Never returns a value, always throws */ FailResult.prototype.unwrap = function () { throw new Error('Cannot unwrap a failure'); }; /** * Extracts the contained Ok value or returns a default. * * Since this is a FailResult, this method always returns the provided default * value because there is no success value to extract. * * @param opt The default value to return * @returns The provided default value */ FailResult.prototype.unwrapOr = function (opt) { return opt; }; /** * Extracts the contained Fail value. * * Since this is a FailResult, this method safely returns the contained error * without any risk of exceptions. * * @returns The contained Fail value */ FailResult.prototype.unwrapFail = function () { return this.failureValue; }; /** * Converts this Fail Result to a None Maybe. * * This method transforms the Fail Result into a Maybe, focusing on the success path. * Since this is a FailResult with no success value, it always returns a None Maybe. * * @returns A None Maybe (always, for FailResult instances) */ FailResult.prototype.maybeOk = function () { return none(); }; /** * Converts this Fail Result to a Some Maybe containing the error value. * * This method transforms the Fail Result into a Maybe, focusing on the failure path. * It always returns a Some Maybe for FailResult instances. * * @returns A Some Maybe containing the error value */ FailResult.prototype.maybeFail = function () { return maybe(this.failureValue); }; /** * Applies the failure branch of a pattern matching object to this Fail Result. * * This method provides a functional way to handle the Fail variant specifically. * For FailResult instances, only the 'fail' function of the pattern is called. * * @typeParam M - The return type of the pattern matching functions * @param fn - An object containing functions to handle each Result variant * @returns The result of applying the 'fail' function to the contained error */ FailResult.prototype.match = function (fn) { return fn.fail(this.failureValue); }; /** * Maps the Fail value using the provided function. * * For FailResult instances, this transforms the contained error using the provided * function and returns a new FailResult with the transformed error. * * @typeParam M - The type of the mapped error * @param fn - A function that transforms the Fail value * @returns A new FailResult with the transformed error */ FailResult.prototype.mapFail = function (fn) { return Result.fail(fn(this.failureValue)); }; /** * Maps the Ok value using the provided function, which is a no-op for FailResult. * * For FailResult instances, this method ignores the mapping function since there is * no success value to transform. It returns a new FailResult with the same error value * but with the new success type parameter. * * @typeParam M - The type of the mapped value (only changes the type parameter) * @returns A new FailResult with the same error and updated success type */ FailResult.prototype.map = function () { return Result.fail(this.failureValue); }; /** * Chains a function that returns another Result, which is a no-op for FailResult. * * For FailResult instances, this method ignores the mapping function since there * is no success value to transform. It returns a new FailResult with the same error * but with the new success type parameter. * * This follows the monadic law that operations on failures should not execute the transformation. * * @typeParam M - The type of the value in the returned Result (only changes the type parameter) * @returns A FailResult with the same error and updated success type */ FailResult.prototype.flatMap = function () { return Result.fail(this.failureValue); }; /** * Short-circuits the flatMapMaybe operation for Fail Results. * * Since this is a Fail Result, the function is not applied and the original error is preserved. * This follows the monadic law that operations on failures should not execute the transformation. * * @returns A Fail Result containing the original error */ FailResult.prototype.flatMapMaybe = function () { return Result.fail(this.failureValue); }; /** * No-op method for consistency with the Result interface. * * For FailResult instances, this method simply returns the current Result * unchanged since it's already a Fail variant. * * @returns This Result unchanged */ FailResult.prototype.toFailWhenOk = function () { return this; }; /** * Converts this Fail Result into a new Fail Result with the provided error value. * * For FailResult instances, this method returns a new Fail Result with the provided * error value, replacing the original error. * * @param val - The new error value to use * @returns A Fail Result containing the provided error */ FailResult.prototype.toFailWhenOkFrom = function (val) { return Result.fail(val); }; /** * Executes a side-effect function for this Fail Result. * * For FailResult instances, this method calls the 'fail' function if provided. * The 'ok' function is never called, even if provided. * * @param val - An object containing optional functions for Ok and Fail variants */ FailResult.prototype.tap = function (val) { typeof val.fail === 'function' && val.fail(this.failureValue); }; /** * No-op method for consistency with the Result interface. * * For FailResult instances, this method does nothing since there is no success * value to operate on. */ FailResult.prototype.tapOk = function () { }; /** * Executes a side-effect function with the contained Fail value. * * For FailResult instances, this method always calls the provided function * with the contained error value. * * @param fn - A function to execute with the Fail value */ FailResult.prototype.tapFail = function (fn) { fn(this.failureValue); }; /** * Executes a side-effect function with the Fail value and returns this Result for chaining. * * For FailResult instances, this method calls the provided function with the * contained error value and then returns the original Result unchanged. * * @param fn - A function to execute with the Fail value * @returns This Result unchanged */ FailResult.prototype.tapFailThru = function (fn) { this.tapFail(fn); return this; }; /** * No-op method that returns this Result for chaining. * * For FailResult instances, this method simply returns the current Result * without calling any function since there is no success value to operate on. * * @returns This Result unchanged */ FailResult.prototype.tapOkThru = function () { return this; }; /** * Executes a side-effect function and returns this Result for chaining. * * For FailResult instances, this method calls the 'fail' function if provided * and then returns the original Result unchanged. * * @param val - An object containing optional functions for Ok and Fail variants * @returns This Result unchanged */ FailResult.prototype.tapThru = function (val) { this.tap(val); return this; }; /** * Transforms this Fail Result into an Ok Result using a recovery function. * * For FailResult instances, this method applies the function to the contained error * to generate a recovery value and returns a new Ok Result with that value. * * @param fn - A function that transforms the Fail value into a recovery value * @returns A new Ok Result with the recovered value */ FailResult.prototype.recover = function (fn) { return Result.ok(fn(this.failureValue)); }; /** * Transforms this Fail Result by applying a function that returns another Result. * * For FailResult instances, this method applies the function to the contained error, * which returns a new Result. * * @param fn - A function that takes the Fail value and returns a new Result * @returns The Result returned by the function */ FailResult.prototype.recoverWith = function (fn) { return fn(this.failureValue); }; /** * Returns the provided fallback Result since this is a Fail variant. * * For FailResult instances, this method returns the provided fallback Result * instead of the current Result. * * @param fallback - The Result to return instead of this one * @returns The provided fallback Result */ FailResult.prototype.orElse = function (fallback) { return fallback; }; /** * Swaps the Ok and Fail values, creating a new Ok Result. * * For FailResult instances, this method transforms the contained error * into a value and returns a new Ok Result with that value. * * @returns A new Ok Result with the error as a value */ FailResult.prototype.swap = function () { return Result.ok(this.failureValue); }; /** * No-op for FailResult, returns this Result unchanged. * * For FailResult instances, this method short-circuits and returns the * current Result without calling the function, since there's no Ok value * to combine with the other Result. * * @returns This Result unchanged */ FailResult.prototype.zipWith = function () { return Result.fail(this.failureValue); }; /** * No-op for FailResult, returns a Promise that resolves to this Result. * * For FailResult instances, this method short-circuits and returns a Promise * that resolves to the current Result without calling the function, since * there's no Ok value to transform. * * @returns A Promise that resolves to this Result */ FailResult.prototype.flatMapPromise = function () { return Promise.resolve(Result.fail(this.failureValue)); }; /** * No-op for FailResult, returns a Promise that resolves to this Result. * * For FailResult instances, this method short-circuits and returns a Promise * that resolves to the current Result without calling the function, since * there's no Ok value to transform. * * @returns A Promise that resolves to this Result */ FailResult.prototype.flatMapObservable = function () { return Promise.resolve(Result.fail(this.failureValue)); }; return FailResult; }(Result)); export { FailResult }; //# sourceMappingURL=result.js.map