typescript-functional-extensions
Version:
A TypeScript implementation of synchronous and asynchronous Maybe and Result monads
300 lines (299 loc) • 9.11 kB
JavaScript
"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;