typescript-functional-extensions
Version:
A TypeScript implementation of synchronous and asynchronous Maybe and Result monads
175 lines (174 loc) • 5.97 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.MaybeAsync = void 0;
const maybe_js_1 = require("./maybe.js");
const resultAsync_js_1 = require("./resultAsync.js");
const unit_js_1 = require("./unit.js");
const utilities_js_1 = require("./utilities.js");
/**
* Represents an asynchronous value that might or might not exist
*/
class MaybeAsync {
static from(valueOrPromiseOrMaybePromise) {
if ((0, utilities_js_1.isPromise)(valueOrPromiseOrMaybePromise)) {
return new MaybeAsync(valueOrPromiseOrMaybePromise.then((v) => v instanceof maybe_js_1.Maybe ? v : maybe_js_1.Maybe.from(v)));
}
else if (valueOrPromiseOrMaybePromise instanceof maybe_js_1.Maybe) {
return new MaybeAsync(Promise.resolve(valueOrPromiseOrMaybePromise));
}
throw new Error('Value must be a Promise or Maybe');
}
/**
* Creates a new MaybeAsync from the given value
* @param value
* @returns
*/
static some(value) {
return new MaybeAsync(Promise.resolve(maybe_js_1.Maybe.some(value)));
}
/**
* Creates a new MaybeAsync with no value
* @returns
*/
static none() {
return new MaybeAsync(Promise.resolve(maybe_js_1.Maybe.none()));
}
get hasValue() {
return this.value.then((m) => m.hasValue);
}
get hasNoValue() {
return this.value.then((m) => m.hasNoValue);
}
value;
constructor(value) {
this.value = value;
}
getValueOrDefault(defaultValueOrFactory) {
if ((0, utilities_js_1.isDefined)(this.value)) {
return this.value.then((m) => {
if ((0, utilities_js_1.isFunction)(defaultValueOrFactory)) {
return m.getValueOrDefault(defaultValueOrFactory());
}
else {
return m.getValueOrDefault(defaultValueOrFactory);
}
});
}
if ((0, utilities_js_1.isFunction)(defaultValueOrFactory)) {
return Promise.resolve(defaultValueOrFactory());
}
return Promise.resolve(defaultValueOrFactory);
}
/**
* Returns the value of the MaybeAsync and throws
* and returns a rejected Promise is there is none
* @returns
*/
getValueOrThrow() {
if ((0, utilities_js_1.isDefined)(this.value)) {
return this.value.then((m) => {
return m.getValueOrThrow();
});
}
return Promise.reject('No value');
}
/**
* Executes the given operator functions, creating a custom pipeline
* @param operations MaybeAsync operation functions
* @returns
*/
pipe(...operations) {
return (0, utilities_js_1.pipeFromArray)(operations)(this);
}
map(projection) {
return MaybeAsync.from(this.value.then(async (m) => {
if (m.hasNoValue) {
return maybe_js_1.Maybe.none();
}
const result = projection(m.getValueOrThrow());
if ((0, utilities_js_1.isPromise)(result)) {
return result.then((r) => maybe_js_1.Maybe.some(r));
}
return maybe_js_1.Maybe.some(result);
}));
}
tap(action) {
return new MaybeAsync(this.value.then(async (m) => {
if (m.hasNoValue) {
return m;
}
const result = action(m.getValueOrThrow());
if ((0, utilities_js_1.isPromise)(result)) {
await result;
}
return m;
}));
}
bind(projection) {
return new MaybeAsync(this.value.then((m) => {
if (m.hasNoValue) {
return maybe_js_1.Maybe.none();
}
const result = projection(m.getValueOrThrow());
if (result instanceof maybe_js_1.Maybe) {
return result;
}
return result.toPromise();
}));
}
match(matcher) {
return this.value.then((m) => m.match(matcher));
}
execute(func) {
return this.value.then((m) => {
if (m.hasNoValue) {
return unit_js_1.Unit.Instance;
}
const result = func(m.getValueOrThrow());
return (0, utilities_js_1.isPromise)(result)
? result.then(() => unit_js_1.Unit.Instance)
: unit_js_1.Unit.Instance;
});
}
or(fallback) {
return new MaybeAsync(this.value.then((m) => {
if (fallback instanceof MaybeAsync) {
return m.orAsync(fallback).toPromise();
}
else {
if (m.hasValue) {
return m;
}
if (!(0, utilities_js_1.isFunction)(fallback)) {
if (fallback instanceof maybe_js_1.Maybe) {
return m.or(fallback);
}
return m.or(fallback);
}
const result = fallback();
if (result instanceof MaybeAsync) {
return m.orAsync(result).toPromise();
}
if (result instanceof maybe_js_1.Maybe) {
return m.or(result);
}
return m.or(result);
}
}));
}
toResult(error) {
return resultAsync_js_1.ResultAsync.from(this.value.then((m) => m.toResult(error)));
}
/**
* Returns the inner Promise, wrapping Maybe.none if handleError is true
* for a rejected Promise, otherwise rejected Promise handling is left up to the caller.
* @param handleError
* @returns
*/
toPromise(handleError = false) {
return (0, utilities_js_1.isDefined)(handleError) && handleError
? this.value.catch((_) => maybe_js_1.Maybe.none())
: this.value;
}
}
exports.MaybeAsync = MaybeAsync;