UNPKG

@rslike/std

Version:

JavaScript Standard library without udndefined behavior!

1,604 lines (1,602 loc) 56.2 kB
// src/utils.ts var UndefinedBehaviorError = class extends Error { }; var customInspectSymbol = Symbol.for("nodejs.util.inspect.custom"); var assertArgument = (method, value, expectedType) => { if (typeof value !== expectedType) { throw new UndefinedBehaviorError( `Method "${String(method)}" should accepts ${expectedType}`, { cause: { value, type: typeof value } } ); } }; // src/option.ts var Status = Object.freeze({ None: "None", Some: "Some" }); var Option = class _Option { value; status; constructor(executor) { const some = (value) => { if (!this.status) { if (value === void 0 || value === null) { this.value = value; this.status = Status.None; } else { this.value = value; this.status = Status.Some; } } }; const none = (reason = void 0) => { if (!this.status) { this.value = reason; this.status = Status.None; } }; assertArgument("constructor", executor, "function"); let executionResult; try { const err = new UndefinedBehaviorError( `You passed an async function in constructor. Only synchronous functions are allowed. Use "Option.fromPromise" or "Option.fromAsync" instead.` ); const executionResult2 = executor(some, none); if (executionResult2 && typeof executionResult2 === "object" && "then" in executionResult2 && typeof executionResult2.then === "function") { throw err; } else if (executionResult2 instanceof _Option) { return executionResult2; } else if (executionResult2 instanceof Result) { if (executionResult2.isOk()) { const unwrapped = executionResult2.unwrap(); if (unwrapped === void 0 || unwrapped === null) { none(unwrapped); } else { some(unwrapped); } } else { none(executionResult2.valueOf()); } } else if (executionResult2 !== void 0 && executionResult2 !== null) { some(executionResult2); } else { } } catch (e) { if (e instanceof UndefinedBehaviorError) { throw e; } none(e); } } /** * Returns the contained `Some` value, consuming the self value. * @example * const x = Some("value"); * x.expect("fruits are healthy") === "value"; // true * * const y: Option<string> = None(); * y.expect("fruits are healthy"); // throws with `fruits are healthy` * @param {string} reason error message * @throws `Error` if value is `null` or `undefined` * @return {*} {Value} */ expect(reason) { assertArgument("expect", reason, "string"); if (this.status === Status.None || this.value === null || this.value === void 0) { throw new Error(reason, { cause: { message: "Option have 'None' status", value: this.value, type: typeof this.value, status: this.status } }); } return this.value; } /** * Returns the contained `Some` value, consuming the self value. * * Because this function may throws, its use is generally discouraged. Instead, prefer to use pattern matching and handle the None case explicitly, or call `unwrapOr`, `unwrapOrElse`, or `unwrapOrDefault`. * * @throws `Error` when value is `None`, `null` or `undefined` * * @example * const x = Some("air"); * x.unwrap() === "air"; * * const x: Option<string> = None(); * x.unwrap() // fails * @return {*} */ unwrap() { if (this.status === Status.None || this.value === null || this.value === void 0) { throw new Error("Unwrap error. Option have 'None' status", { cause: { status: this.status, value: this.value } }); } return this.value; } /** * Returns the contained `Some` value or a provided default. * @example * const x = Some("air"); * x.unwrapOr("another") === "air"; * * const x: Option<string> = None(); * x.unwrapOr("another") === 'another' * @param {T} fallback fallback value * @return {*} {Value} */ unwrapOr(fallback) { if (this.status === Status.None || this.value === null || this.value === void 0) { return fallback; } return this.value; } /** * Returns the contained `Some` value or computes it from a closure. * @example * const k = 10; * Some(4).unwrapOrElse(() => 2 * k) === 4 * None().unwrapOrElse(() => 2 * k) === 20 * @param predicate function to call when `Option` is `None` * @throws `UndefinedBehaviorError` if `predicate` is not a function * @return Unwrapped value or prdicate result */ unwrapOrElse(predicate) { if (this.status === Status.None) { assertArgument("unwrapOrElse", predicate, "function"); return predicate(); } return this.unwrap(); } /** * Maps an `Option<T>` to `Option<U>` by applying a function to a contained value (if `Some`) or returns `None` (if `None`). * * @example * const maybeSomeString = Some("Hello, World!"); * const maybeSomeLen = maybeSomeString.map(s => s.length); * maybeSomeLen === Some(13)); * * const x: Option<string> = None(); * x.map(s => s.length) === None(); * @template U * @param predicate function to evaluate when `Option` have `Some` value * @throws `UndefinedBehaviorError` if `predicate` is not a function * @return `Option` instance */ map(predicate) { if (this.status === Status.Some) { assertArgument("map", predicate, "function"); return Some(predicate(this.value)); } return None(); } /** * Returns the provided default result (if none), or applies a function to the contained value (if any). * * Arguments passed to `mapOr` are eagerly evaluated; if you are passing the result of a function call, it is recommended to use `mapOrElse`, which is lazily evaluated. * * @example * const x = Some("foo"); * x.mapOr(42, v => v.length) === 3; * * const x: Option<string> = None(); * x.mapOr(42, v => v.len() === 42; * @param {U} fallback fallback value returns when `Option` have `None` status * @param predicate function to evaluate when `Option` have `Some` status * @throws `UndefinedBehaviorError` if `predicate` is not a function * @return */ mapOr(fallback, predicate) { if (this.status === Status.None) { return fallback; } assertArgument("mapOr", predicate, "function"); return predicate(this.value); } /** * Computes a default function result (if none), or applies a different function to the contained value (if any). * * @example * const k = 21; * * const x = Some("foo"); * x.mapOrElse(() => 2 * k, v => v.length) === 3; * * const x: Option<string> = None(); * x.mapOrElse(() => 2 * k, v => v.length) === 42; * @template U * @throws `UndefinedBehaviorError` if `noneFn` is not a function * @throws `UndefinedBehaviorError` if `someFn` is not a function * @return {*} {U} */ mapOrElse(noneFn, someFn) { assertArgument("mapOrElse", noneFn, "function"); assertArgument("mapOrElse", someFn, "function"); if (this.status === Status.None) { return noneFn(); } return someFn(this.value); } /** * Transforms the `Option<T>` into a `Result<T, E>`, mapping `Some(v)` to `Ok(v)` and None to `Err(err)`. * * Arguments passed to `okOr` are eagerly evaluated; if you are passing the result of a function call, it is recommended to use `okOrElse`, which is lazily evaluated. * * @example * const x = Some("foo"); * String(x.okOr(0)) === String(Ok("foo")); * * const y: Option<string> = None(); * y.okOr(0) === Err(0); */ okOr(err) { if (this.status === Status.None) { return Err(err); } return Ok(this.value); } /** * Transforms the `Option<T>` into a `Result<T, E>`, mapping `Some(v)` to `Ok(v)` and None to `Err(err())`. * * @example * const x = Some("foo"); * console.assert(x.okOrElse(() => 0) === Ok("foo")); * * let y: Option<string> = None(); * console.assert(y.okOrElse(() => 0) === Err(0)); * @return {*} {Value} */ okOrElse(err) { if (this.status === Status.None) { assertArgument("okOrElse", err, "function"); return Err(err()); } return Ok(this.value); } /** * Returns `None` if the option is `None`, otherwise returns `optb`. * * Arguments passed to and are eagerly evaluated; if you are passing the result of a function call, it is recommended to use `andThen`, which is lazily evaluated. * @throws `UndefinedBehaviorError` if `optb` is not an instance of `Option` * @example * const x = Some(2); * const y: Option<string> = None(); * console.assert(x.and(y) === None()); * // another example * let x: Option<number> = None(); * let y = Some("foo"); * console.assert(x.and(y) === None()); * // another example * let x = Some(2); * let y = Some("foo"); * console.assert(x.and(y) === Some("foo")); * // another example * let x: Option<number> = None(); * let y: Option<string> = None(); * console.assert(x.and(y) === None()); */ and(optb) { if (this.status === Status.None) { return None(); } if (!(optb instanceof _Option)) { throw new UndefinedBehaviorError( `Method "and" should accepts instance of Option`, { cause: { value: optb, type: typeof optb } } ); } return optb; } /** * Returns `None` if the option is `None`, otherwise calls `f` with the wrapped value and returns the result. * * Some languages call this operation flatmap. * * @example * function toString(x: number): Option<string> { * return Some(String(x)); * } * console.assert(Some(2).andThen(toString) === Some(2.toString())); * console.assert(None().andThen(toString) === None()); * @template U * @throws `UndefinedBehaviorError` if `predicate` is not a function * @throws `UndefinedBehaviorError` if return type of `predicate` is not an instance of `Option` * @return {*} {Option<U>} */ andThen(predicate) { if (this.status === Status.None) { return None(); } assertArgument("andThen", predicate, "function"); const res = predicate(this.value); if (!(res instanceof _Option)) { throw new UndefinedBehaviorError( 'callback for Method "andThen" expects to returns instance of Option. Use "None" or "Some" funtions', { cause: { value: res, type: typeof res } } ); } return res; } /** * Returns `None` if the option is `None`, otherwise calls predicate with the wrapped value and returns: * * - `Some(t)` if predicate returns `true` (where t is the wrapped value), an * - `None` if predicate returns `false` * * @example * function isEven(n: number): boolean { * return n % 2 == 0 * } * console.assert(None().filter(isEven) === None()); * console.assert(Some(3).filter(isEven) === None()); * console.assert(Some(4).filter(isEven) === Some(4)); * @throws `UndefinedBehaviorError` if `predicate` is not a function * @throws `UndefinedBehaviorError` if return type of `predicate` function is not a `boolean` * @param predicate * @return {*} {Option<Value>} */ filter(predicate) { if (this.status === Status.None) { return None(); } assertArgument("filter", predicate, "function"); const success = predicate(this.value); assertArgument("filter", success, "boolean"); if (success) { return Some(this.value); } return None(); } /** * Returns `Some` if exactly one of self, optb is `Some`, otherwise returns `None`. * @throws `UndefinedBehaviorError` if `optb` is not an instance of `Option` * @param optb * @return {*} {Option<Value>} */ xor(optb) { if (this.status === Status.Some) { return this; } if (!(optb instanceof _Option)) { throw new UndefinedBehaviorError( `Method "xor" should accepts instance of Option`, { cause: { value: optb } } ); } return optb; } /** * Inserts value into the option, then returns a mutable reference to it. * * If the option already contains a value, the old value is dropped. * * See also `getOrInsert`, which doesn’t update the value if the option already contains `Some`. * @see {@link Option.getOrInsert} * @example * const opt = None(); * const val = opt.insert(1); * console.assert(val === 1); * console.assert(opt.unwrap() === 1); * // another example * const val = opt.insert(2); * console.assert(val === 2); * * @param value * @return {*} {Option<Value>} */ insert(value) { if (value === void 0) { this.status = Status.None; this.value = void 0; } else if (value === null) { this.status = Status.None; this.value = null; } else { this.status = Status.Some; this.value = value; } return this; } /** * Replaces the actual value in the option by the value given in parameter, returning the old value if present, leaving a `Some` in its place without deinitializing either one. * * @example * const x = Some(2); * const old = x.replace(5); * console.assert(x === Some(5)); * console.assert(old === Some(2)); * // another example * const x = None(); * const old = x.replace(3); * console.assert(x === Some(3)); * console.assert(old === None()); * @param {T} value * @return {*} {Option<Value>} */ replace(value) { const oldValue = Some(this.value); const newValue = Some(value); if (newValue.isSome()) { this.value = newValue.unwrap(); this.status = Status.Some; } else { this.value = newValue.valueOf(); this.status = Status.None; } return oldValue; } /** * Zips self with another Option. * * If self is `Some(s)` and other is `Some(o)`, this method returns `Some((s, o))`. Otherwise, `None` is returned. * * @example * const x = Some(1); * const y = Some("hi"); * const z = None<number>(); * * x.zip(y) === Some((1, "hi")); * x.zip(z) === None(); * @template U * @param {Option<U>} other * @throws `UndefinedBehaviorError` if `other` is not an instance of `Option` * @return {*} {Option<[Value, U]>} */ zip(other) { if (!(other instanceof _Option)) { throw new UndefinedBehaviorError( `Method "zip" should accepts instance of Option`, { cause: { value: other } } ); } if (this.status === Status.Some && other.status === Status.Some) { return new _Option((some) => some([this.value, other.value])); } return None(); } /** * Zips self and another Option with function `f`. * * If self is `Some(s)` and other is `Some(o)`, this method returns `Some(f(s, o))`. Otherwise, `None` is returned. * * @example * class Point { * constructor (readonly x: number, readonly y: number){} * static create(x:number, y: number){ * return new Point(x,y); * } * } * const x = Some(17.5); * const y = Some(42.7); * * x.zipWith(y, Point.create) === Some({ x: 17.5, y: 42.7 }) * @throws `UndefinedBehaviorError` if `other` is not an instance of `Option` * @throws `UndefinedBehaviorError` if `predicate` is not a function * @template U * @template R * @param {Option<U>} other * @param predicate * @return {*} {Option<R>} */ zipWith(other, predicate) { if (!(other instanceof _Option)) { throw new UndefinedBehaviorError( `Method "zipWith" should accepts instance of Option`, { cause: { value: other, type: typeof other } } ); } assertArgument("zipWith", predicate, "function"); if (this.status === Status.Some && other.status === Status.Some) { return Some(predicate(this.value, other.value)); } return None(); } /** * Unzips an option containing a tuple of two options. * * If self is `Some((a, b))` this method returns `(Some(a), Some(b))`. Otherwise, `(None, None)` is returned. * * @example * const x = Some([1, "hi"]); * const y = None<[number, number]>(); * console.assert(x.unzip() === [Some(1), Some("hi")]); * console.assert(y.unzip() === [None(), None()]); */ unzip() { if (Array.isArray(this.value) && this.value.length === 2) { return [Some(this.value.at(0)), Some(this.value.at(1))]; } return [None(), None()]; } /** * Converts from `Option<Option<T>>` to `Option<T>`. * @example * const x: Option<Option<number>> = Some(Some(6)); * Some(6) === x.flatten(); * * const x: Option<Option<number>> = Some(None()); * None() === x.flatten(); * * const x: Option<Option<number>> = None(); * None() === x.flatten() * @return {*} {Value extends Option<infer Sub> ? Option<Sub> : Option<Value>} */ flatten() { if (this.value instanceof _Option) { return Some(this.value.value); } return Some(this.value); } /** * Some value of type `T`. */ static Some(value = void 0) { if (value === void 0 || value === null) { return new _Option((_, none) => none(value)); } return new _Option((some) => some(value)); } /** * No value. */ static None(value = void 0) { return new _Option((_, none) => none(value)); } /** represents no value */ static none = _Option.None; /** represents some value (non `null` or `undefined`) */ static some = _Option.Some; /** * Creates an Option instance along with its associated `some` and `none` functions. * @returns An object containing the resolve function, reject function, and the Option instance. * @example * const { resolve, reject, option } = Option.withResolvers<number, string>(); * resolve(3); * option.then(value => console.log(value)); // Outputs: 3 */ static withResolvers() { let some; let none; const option = new _Option((ok, err) => { some = ok; none = err; }); return { some, none, option }; } static Status = Status; /** * Returns `true` if incoming `value` is instance of `Option`. * * @static * @param {unknown} value * @return {*} * @memberof Ordering */ static is(value) { return value instanceof _Option; } /** * Await a promise and return an `Option` instance. * @param promiseLike promise to await * @returns */ static async fromPromise(promiseLike) { let option; try { const v = await promiseLike; option = Some(v); } catch (error) { if (error === void 0 || error === null) { option = None(error); } else { option = None(); } } return option; } static fromAsync = this.fromPromise; /** * Compare Self and another value. * You can pass your own function to compare * @example * const a = Some(2) * const b = 2 * const same = a.equal(b, (result, another) => { * // result = Some(2) * // another = 2 * return result.unwrap() === another * }) * console.log(same) // true * console.log(a.equal(b)) // false * console.log(a.equal(Some(2))) // true * @param other another value * @param [cmp=Object.is] compare function. Default - `Object.is` */ equal(other, cmp = Object.is) { if (other instanceof _Option) { return cmp(this.value, other.value); } return cmp(this, other); } /** * Returns `true` if the option is a `Some` value. * * @example * const x: Option<number> = Some(2); * x.isSome() === true // true * * const x: Option<number> = None(); * x.isSome() === false // true * @return {*} {boolean} */ isSome() { return this.status === Status.Some; } /** * Returns true if the option is a `None` value. * * @return {*} {boolean} */ isNone() { return this.status === Status.None; } /** * Returns `true` if the option is a `Some` and the value inside of it matches a predicate. * @throws `UndefinedBehaviorError` if predicate is not a function * @throws `UndefinedBehaviorError` if predicate return type is not a `boolean` * @example * const x: Option<number> = Some(2); * x.isSomeAnd(x => x > 1) === true // true * * const x: Option<number> = Some(0); * x.isSomeAnd(x => x > 1 ) === false // true * * const x: Option<number> = None(); * x.isSomeAnd(x => x > 1 ) === false // true * @param predicate */ isSomeAnd(predicate) { assertArgument("isSomeAnd", predicate, "function"); if (this.status === Status.Some) { const res = predicate(this.value); assertArgument("isSomeAnd", res, "boolean"); return res; } return false; } /** * Inserts value into the option if it is `None`, then returns a mutable reference to the contained value. * * See also `insert`, which updates the value even if the option already contains `Some`. * @see {@link Option.insert insert method} * @throws `UndefinedBehaviorError` if incoming `value` is `null` or `undefined` * @example * const x = None<number>(); * const y = x.getOrInsert(7); * * y === 7 // true * @param {T} value * @return {*} {Value} */ getOrInsert(value) { if (this.status === Status.None) { if (value === void 0) { throw new UndefinedBehaviorError( `Method "getOrInsert" should provide non "undefined" value.` ); } else if (value === null) { throw new UndefinedBehaviorError( `Method "getOrInsert" should provide non "null" value.` ); } return this.insert(value).unwrap(); } return this.value; } /** * Inserts a value computed from `predicate` into the option if it is `None`, then returns the contained value. * @throws `UndefinedBehaviorError` if incoming `predicate` type is not a function * @throws `UndefinedBehaviorError` if incoming `predicate` returns `null` or `undefined` * @example * const x = None<number>(); * const y = x.getOrInsertWith(() => 5); * * y === 5 // true * * @param predicate * @return {*} {Value} */ getOrInsertWith(predicate) { if (this.status === Status.None) { assertArgument("getOrInsertWith", predicate, "function"); const res = predicate(); if (res === void 0) { throw new UndefinedBehaviorError( "Callback for method 'getOrInsertWith' should returns non 'undefined' value." ); } else if (res === null) { throw new UndefinedBehaviorError( "Callback for method 'getOrInsertWith' should returns non 'null' value." ); } return this.insert(res).unwrap(); } return this.value; } /** * Returns the `Option` if it contains a value, otherwise returns `optb`. * Arguments passed to or are eagerly evaluated; if you are passing the result of a function call, it is recommended to use `orElse`, which is lazily evaluated. * * @throws `UndefinedBehaviorError` if incoming `optb` is not an instance of `Option` * @example * const x = Some(2); * const y = None(); * console.assert(x.or(y) === Some(2)); * // another example * const x = None(); * const y = Some(100); * console.assert(x.or(y) === Some(100)); * // another example * let x = Some(2) * let y = Some(100) * console.assert(x.or(y) === Some(2)); * // another example * const x: Option<number> = None(); * const y = None(); * console.assert(x.or(y) === None()); * * @param {Option<T>} optb * @return {*} {Option<Value>} */ or(optb) { if (this.status === Status.Some) { return this; } if (!(optb instanceof _Option)) { throw new UndefinedBehaviorError( `Method "or" should accepts isntance of "Option"`, { cause: { value: optb, type: typeof optb } } ); } return optb; } /** * Returns the `Option` if it contains a value, otherwise calls `f` and returns the result. * * @throws `UndefinedBehaviorError` if incoming `predicate` is not a function * @throws `UndefinedBehaviorError` if incoming `predicate` returns not an isntance of `Option` * @example * function nobody(): Option<string> { return None() } * function vikings(): Option<string> { return Some("vikings") } * * Some("barbarians").orElse(vikings) === Some("barbarians"); // true * None().orElse(vikings) === Some("vikings"); // true * None().orElse(nobody) === None(); // true * * @param predicate * @return {*} {Option<Value>} */ orElse(predicate) { if (this.status === Status.Some) { return this; } assertArgument("orElse", predicate, "function"); const result = predicate(); if (!(result instanceof _Option)) { throw new UndefinedBehaviorError( `Callback result for method "orElse" should returns instance of Option. Use "Some" or "None".`, { cause: { value: result, type: typeof result } } ); } return result; } /** * Transposes an `Option` of a `Result` into a Result of an Option. * * `None` will be mapped to `Ok(None)`. * `Some(Ok(_))` and `Some(Err(_))` will be mapped to `Ok(Some(_))` and `Err(_)`. * @throws `UndefinedBehaviorError` if unwrapped value is not an instance of `Result` */ transpose() { if (this.isNone()) { return Ok(None()); } const value = this.unwrap(); if (value instanceof Result) { if (value.isOk()) { return Ok(Some(value.unwrap())); } return value; } throw new UndefinedBehaviorError( `no method named "transpose" found for class "Result<${typeof this.value}, _>" in the current scope` ); } /** * Returns raw value. * @note This method is used for internal purposes and should not be used directly. */ valueOf() { return this.value; } toString() { return `${this.status}(${this.status === Status.None ? "" : this.value})`; } /** * This internal method using for `JSON.stringify` serialization. Please avoid using this method directly. * @internal * @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#tojson_behavior MDN. toJSON behavior} */ toJSON() { return { status: this.status, value: this.value }; } /** * @protected */ [Symbol.toPrimitive]() { return this.value; } /** * @protected */ get [Symbol.toStringTag]() { return "Option"; } /** * @protected * Iterator support for `Option`. * * _Note: This method will only yeild if the Option is Some._ */ [Symbol.iterator]() { if (this.isSome() && typeof this.value === "object" && this.value !== null && typeof this.value[Symbol.iterator] === "function") { return this.value[Symbol.iterator](); } throw new UndefinedBehaviorError( "[Symbol.iterator] can applies only for Some(<Iterable>) value", { cause: { value: this.value, type: typeof this.value, status: this.status } } ); } [Symbol.split](string, limit) { if (this.isSome() && typeof this.value === "string") { return string.split(this.value, limit); } throw new UndefinedBehaviorError( "[Symbol.split] can applies only for Some(<string>) value", { cause: { value: this.value, type: typeof this.value, status: this.status } } ); } [Symbol.search](string) { if (this.isSome() && typeof this.value === "string") { return string.indexOf(this.value); } throw new UndefinedBehaviorError( "[Symbol.search] can applies only for Some(<string>) value", { cause: { value: this.value, type: typeof this.value, status: this.status } } ); } /** * @protected * Iterator support for `Option`. * * _Note: This method will only yeild if the Option is Some._ */ [Symbol.asyncIterator]() { if (this.isSome() && typeof this.value === "object" && this.value !== null && typeof this.value[Symbol.asyncIterator] === "function") { return this.value[Symbol.asyncIterator](); } throw new UndefinedBehaviorError( "[Symbol.asyncIterator] can applies only for Some(<AsyncIterable>) value", { cause: { value: this.value, type: typeof this.value, status: this.status } } ); } [customInspectSymbol](depth, options, inspect) { const name = this.status; const primitive = this[Symbol.toPrimitive](); if (depth < 0) { return `${options.stylize(name, "special")}(${primitive})`; } const newOptions = Object.assign({}, options, { depth: options.depth === null ? null : options.depth - 1 }); const inner = this.value !== void 0 ? inspect(this.value, newOptions).replace(/\n/g, "\n$") : ""; return `${options.stylize(name, "special")}(${inner})`; } }; function Some(value = void 0) { return Option.Some(value); } Object.defineProperty(Some, Symbol.hasInstance, { value: (instance) => { if (typeof instance !== "object") return false; const instanceOfOption = instance instanceof Option; if (instanceOfOption === false) { return false; } return instance.isSome(); } }); function None(value = void 0) { return Option.None(value); } Object.defineProperty(None, Symbol.hasInstance, { value: (instance) => { if (typeof instance !== "object") return false; const instanceOfOption = instance instanceof Option; if (instanceOfOption === false) { return false; } return instance.isNone(); } }); // src/result.ts var Status2 = Object.freeze({ Err: "Err", Ok: "Ok" }); var Result = class _Result { value = null; error = void 0; status; constructor(executor) { const okFn = (value) => { if (!this.status) { this.value = value ?? null; this.status = Status2.Ok; } }; const errorFn = (error) => { if (!this.status) { this.error = error; this.status = Status2.Err; } }; assertArgument("constructor", executor, "function"); let executionResult; try { executionResult = executor(okFn, errorFn); if (executionResult && typeof executionResult === "object" && "then" in executionResult && typeof executionResult.then === "function") { const err = new UndefinedBehaviorError( `You passed an async function in constructor or executor returned a promise. Only synchronous functions are allowed. Use "Result.fromPromise" or "Result.fromAsync" instead.` ); executionResult.then(() => { throw err; }); throw err; } else if (executionResult instanceof _Result) { return executionResult; } else if (executionResult instanceof Option) { if (executionResult.isSome()) { okFn(executionResult.unwrap()); } else { errorFn(executionResult.valueOf()); } } else if (executionResult !== void 0) { okFn(executionResult); } } catch (err) { if (err instanceof UndefinedBehaviorError && executionResult && typeof executionResult === "object" && "then" in executionResult && typeof executionResult.then === "function") { throw err; } else { errorFn(err); } } } /** * Returns the contained `Ok` value, consuming the self value. * * Because this function may throws, its use is generally discouraged. Call `unwrapOr`, `unwrapOrElse`. * * Panics if the value is an `Err`, with a message including the passed message, and the content of the `Err`. * * @example * const x: Result<number, string> = Err("emergency failure"); * x.expect("Testing expect"); // `Testing expect`, cause: emergency failure * @throws `Error` if `Result` has error status * @param {string} reason * @return {*} {T} */ expect(reason) { if (reason && this.status === Status2.Err) { throw new Error(reason, { cause: this.error }); } return this.value; } /** * Returns the contained `Ok` value, consuming the self value. * * Because this function may throws, its use is generally discouraged. Instead, call `unwrapOr`, `unwrapOrElse`. * * @example * const x: Result<number, string> = Ok(2); * x.unwrap() === 2; * @return {*} {T} */ unwrap() { if (this.status === Status2.Ok) { return this.value; } throw this.error; } /** * Returns the contained `Ok` value or a provided default. * * Arguments passed to `unwrapOr` are eagerly evaluated; if you are passing the result of a function call, it is recommended to use `unwrapOrElse`, which is lazily evaluated. * * @example * const fallback = 2; * const x = Ok(9); * x.unwrapOr(fallback) === 9; // true * * cosnt x: Result<number, string> = Err("error"); * x.unwrapOr(fallback) === fallback; // true * @param {TInput} fallback * @return {*} {T} */ unwrapOr(fallback) { if (this.status === Status2.Ok) { return this.value; } return fallback; } /** * Returns `true` if the result is `Ok`. * * @example * const x: Result<number, string> = Ok(-3); * x.isOk() // true * // another example * let x: Result<number, string> = Err("Some error message"); * x.isOk() // false * @return {*} {boolean} */ isOk() { return this.status === Status2.Ok; } /** * Returns `true` if the result is `Ok` and the value inside of it matches a predicate. * * @throws `UndefinedBehaviorError` if `predicate` is not a function * @throws `UndefinedBehaviorError` if `predicate` result is not a boolean * @example * const x: Result<number, string> = Ok(2); * console.assert(x.isOkAnd(x => x > 1) === true); * // another example * const x: Result<number, string> = Ok(0); * console.assert(x.isOkAnd(x => x > 1) === false); * // another example * const x: Result<number, string> = Err("hey"); * console.assert(x.isOkAnd(x => x > 1) === false); * @return {*} {boolean} */ isOkAnd(predicate) { if (this.status === Status2.Err) { return false; } assertArgument("isOkAnd", predicate, "function"); const res = predicate(this.value); assertArgument("isOkAnd", res, "boolean"); return res; } /** * Returns `true` if the result is `Err`. * * @example * const x: Result<number, string> = Ok(-3); * console.assert(x.isErr() === false); * // another example * const x: Result<number, string> = Err("Some error message"); * console.assert(x.isErr() === true); * * @return {*} {boolean} */ isErr() { return this.status === Status2.Err; } /** * Returns `true` if the result is `Err` and the value inside of it matches a predicate. * @example * const x: Result<number, Error> = Err(new Error("not found")); * x.isErrAnd(e => e.message === 'not found') // true; * // another example * const x: Result<number, Error> = Err(new Error('permission denied')); * x.isErrAnd(x => x.name === 'TypeError') // false * // another example * const x: Result<number, Error> = Ok(123); * x.isErrAnd(e => e.name == 'Error'); // false * @throws `UndefinedBehaviorError` if `predicate` is not a function * @throws `UndefinedBehaviorError` if `predicate` result is not a boolean * @param {(err: TErr) => boolean} predicate * @return {*} {boolean} */ isErrAnd(predicate) { if (this.status === Status2.Ok) { return false; } assertArgument("isErrAnd", predicate, "function"); const res = predicate(this.err); assertArgument("isErrAnd", res, "boolean"); return res; } /** * Converts from `Result<T, E>` to `Option<T>`. * * Converts self into an `Option<T>`, consuming self, and discarding the error, if any. * * @example * const x: Result<number, string> = Ok(2); * x.ok() === Some(2); // true * // another example * const x: Result<number, string> = Err("Nothing here"); * x.ok() === None(); // true * @return {*} {Option<T>} */ ok() { if (this.status === Status2.Ok) { return Some(this.value); } return None(); } /** * Converts from `Result<T, E>` to `Option<E>`. * * Converts self into an `Option<E>`, consuming self, and discarding the success value, if any. * * @example * const x: Result<number, string> = Ok(2); * x.err() === None(); // true * * const x: Result<number, string> = Err("Nothing here"); * x.err() === Some("Nothing here"); // true * @return {*} {Option<E>} */ err() { if (this.status === Status2.Err) { return Some(this.error); } return None(); } /** * Maps a `Result<T, E>` to `Result<U, E>` by applying a function to a contained Ok value, leaving an `Err` value untouched. * * This function can be used to compose the results of two functions. * * @example * const x = Ok(1); * x.map(v => v * 2) === Ok(2) // true * * @template U * @throws `UndefinedBehaviorError` if `mapFn` is not a function * @param {(value: TInput) => U} mapFn * @return {*} {Result<U, E>} */ map(mapFn) { assertArgument("map", mapFn, "function"); if (this.status === Status2.Ok) { return Ok(mapFn(this.value)); } return this; } /** * Returns the provided default (if `Err`), or applies a function to the contained value (if `Ok`), * * Arguments passed to `mapOr` are eagerly evaluated; if you are passing the result of a function call, it is recommended to use `mapOrElse`, which is lazily evaluated. * * @example * const x: Result<string, string> = Ok("foo"); * x.mapOr(42, v => v.length) // result is 3 * // another example * const x: Result<number, string> = Err("bar"); * x.mapOr(42, v => v.length) // 42 * @throws `UndefinedBehaviorError` if `predicate` is not a function * @template U * @param {U} another * @param {(value: TInput) => U} predicate * @return {*} {U} */ mapOr(another, predicate) { assertArgument("mapOr", predicate, "function"); if (this.status === Status2.Ok) { return predicate(this.value); } return another; } /** * Maps a `Result<T, E>` to `U` by applying fallback function default to a contained `Err` value, or function `f` to a contained `Ok` value. * * This function can be used to unpack a successful result while handling an error. * * @example * let k = 21; * * const x: Result<string, string> = Ok("foo"); * x.mapOrElse(err => k * 2, v => v.length); // 3 * * const y : Result<string, string> = Err("bar"); * y.mapOrElse(e => k * 2, v => v.length) // 42 * @throws `UndefinedBehaviorError` if `errFn` is not a function * @throws `UndefinedBehaviorError` if `okFn` is not a function * @template U * @param errFn * @param okFn * @return {*} {U} */ mapOrElse(errFn, okFn) { assertArgument("mapOrElse", errFn, "function"); if (this.status === Status2.Err) { return errFn(this.error); } assertArgument("mapOrElse", okFn, "function"); return okFn(this.value); } /** * Maps a `Result<T, E>` to `Result<T, F>` by applying a function to a contained `Err` value, leaving an `Ok` value untouched. * * This function can be used to pass through a successful result while handling an error. * * @example * const stringify = (x: number) => `error code: ${x}` * * const x: Result<number, number> = Ok(2); * x.mapErr(stringify) === Ok(2) // true * * const y: Result<number, number> = Err(13); * y.mapErr(stringify) === Err("error code: 13")); * @template F * @throws `UndefinedBehaviorError` if `errFn` is not a function * @param {(err: TErr) => F} errFn * @return {*} {Result<T, F>} */ mapErr(errFn) { assertArgument("mapErr", errFn, "function"); if (this.status === Status2.Err) { return Err(errFn(this.error)); } return this; } /** * Returns the contained `Err` value, consuming the self value. * * @example * const x: Result<number, string> = Ok(10); * x.expectErr("Testing expectErr"); // throws `Testing expectErr; cause: 10` * @throws `Error` if `Result` status is OK * @param {string} reason * @return {*} {E} */ expectErr(reason) { assertArgument("expectErr", reason, "string"); if (this.status === Status2.Ok) { throw new Error(reason, { cause: this.value }); } return this.error; } /** * Returns the contained `Err` value, consuming the self value. * * @example * const x: Result<number, string> = Err("emergency failure"); * x.unwrapErr() === "emergency failure"; * @throws `value` if `Result` status is OK * @return {*} {E} */ unwrapErr() { if (this.status === Status2.Err) { return this.error; } throw this.value; } /** * Returns the contained `Ok` value or computes it from a closure. * * @example * const count = (x: string) => x.length; * * Ok(2).unwrapOrElse(count) === 2 // true * Err("foo").unwrapOrElse(count) === 3; // true * @throws `UndefinedBehaviorError` if `predicate` is not a function * @param predicate * @return {*} {T} */ unwrapOrElse(predicate) { if (this.status === Status2.Ok) { return this.value; } assertArgument("unwrapOrElse", predicate, "function"); return predicate(this.error); } /** * Returns `res` if the result is `Ok`, otherwise returns the `Err` value of self. * * Arguments passed to and are eagerly evaluated; if you are passing the result of a function call, it is recommended to use `andThen`, which is lazily evaluated. * * @example * const x: Result<number, string> = Ok(2); * const y: Result<string, string> = Err("late error"); * x.and(y) === Err("late error"); // true * // another example * const x: Result<number, string> = Err("early error"); * const y: Result<string, string> = Ok("foo"); * x.and(y) === Err("early error"); // true * // another example * const x: Result<number, string> = Err("not a 2"); * const y: Result<string, string> = Err("late error"); * x.and(y) === Err("not a 2"); // true * // another example * const x: Result<number, string> = Ok(2); * const y: Result<string, string> = Ok("different result type"); * x.and(y) === Ok("different result type"); // true * @template U * @throws `UndefinedBehaviorError` if `res` is not an instance of `Result` * @param res * @return {*} {Result<U, E>} */ and(res) { if (!(res instanceof _Result)) { throw new UndefinedBehaviorError( `Method "and" should accepts instance of Result`, { cause: { value: res } } ); } if (this.status === Status2.Err) { return Err(this.error); } return res; } /** * Calls op if the result is `Ok`, otherwise returns the `Err` value of self. * * This function can be used for control flow based on `Result` values. * * @example * const sqThenToString = (x: number) => { * return Ok(x * x).map(sq => sq.toString()) * } * * Ok(2).andThen(sqThenToString) === Ok(4.toString())); // true * Err("not a number").andThen(sqThenToString) === Err("not a number"); // true * @template U * @throws `UndefinedBehaviorError` if `fn` argument is not a function * @throws `UndefinedBehaviorError` if `fn` result is not an instance of `Result` * @param {(value: TInput) => Result<U, TErr>} fn * @return {*} {Result<U, E>} */ andThen(fn) { assertArgument("andThen", fn, "function"); if (this.status === Status2.Ok) { const res = fn(this.value); if (res instanceof _Result) { return res; } throw new UndefinedBehaviorError( "Function result expected to be instance of Result.", { cause: res } ); } return Err(this.error); } /** * Returns `res` if the result is `Err`, otherwise returns the `Ok` value of self. * * Arguments passed to or are eagerly evaluated; if you are passing the result of a function call, it is recommended to use `orElse`, which is lazily evaluated. * * @example * const x: Result<number, string> = Ok(2); * const y: Result<number, string> = Err("late error"); * x.or(y) === Ok(2); // true * // another example * const x: Result<number, string> = Err("early error"); * const y: Result<number, string> = Ok(2); * x.or(y) === Ok(2); // true * // another example * const x: Result<number, string> = Err("not a 2"); * const y: Result<number, string> = Err("late error"); * x.or(y) === Err("late error"); // true * // another example * const x: Result<number, string> = Ok(2); * const y: Result<number, string> = Ok(100); * x.or(y) === Ok(2); // true * @template F * @throws `UndefinedBehaviorError` if `res` argument is not an instance of `Result` * @param {Result<TInput, F>} res * @return {*} {Result<T, F>} */ or(res) { if (!(res instanceof _Result)) { throw new UndefinedBehaviorError( `Operator "or" expect to pass instance of Result`, { cause: { value: res } } ); } if (this.status === Status2.Err) { return res; } return this; } /** * Calls `fn` if the result is `Err`, otherwise returns the `Ok` value of self. * * This function can be used for control flow based on result values. * * @example * const sq = (x: number) => Ok(x * x); * const err = (x: number) => Err(x); * * Ok(2).orElse(sq).orElse(sq) === Ok(2); // true * Ok(2).orElse(err).orElse(sq) === Ok(2); // true * Err(3).orElse(sq).orElse(err) === Ok(9); // true * Err(3).orElse(err).orElse(err) === Err(3); // true * @throws `UndefinedBehaviorError` if `fn` argument is not a function * @throws `UndefinedBehaviorError` if `fn` result is not an instance of `Result` * @param fn * @return {*} {Result<T, F>} */ orElse(fn) { if (this.status === Status2.Ok) { return this; } assertArgument("orElse", fn, "function"); const res = fn(this.error); if (!(res instanceof _Result)) { throw new UndefinedBehaviorError( 'Operator "orElse" expected to return instance of Result. Use "Ok" or "Err" function to define them.', { cause: { value: res, type: typeof res } } ); } return res; } /** * Converts from `Result<Result<T, E>, E>` to `Result<T, E>` * * @example * const x: Result<Result<string, number>, number> = Ok(Ok("hello")); * Ok("hello") === x.flatten() // true * * const x: Result<Result<string, number>, number> = Ok(Err(6)); * Err(6) === x.flatten(); // true * * const x: Result<Result<string, number>, number> = Err(6); * Err(6) === x.flatten(); // true * @return {*} {T extends Result<infer Ok, E> ? Result<Ok, E> : Result<T, E>} */ flatten() { if (this.value instanceof _Result) { return this.value; } return this; } static Ok(value) { return new _Result((ok) => ok(value)); } static Err(value) { return new _Result((_, rej) => rej(value)); } static ok = _Result.Ok; static err = _Result.Err; /** * Similar to `Promise.withResolvers` API. * @example * const {ok, result} = Result.withResolvers() * ok(3) * result.unwrap() // 3 * result.isOk() // true * */ static withResolvers() { var ok; var err; const result = new _Result((res, rej) => { ok = res; err = rej; }); return { ok, err, result }; } /** * Returns `true` if incoming `value` is instance of `Result`. * * @static * @param {unknown} value * @return {*} * @memberof Ordering */ static is(value) { return value instanceof _Result; } static Status = Status2; /** * Await a promise and return a Result instance. * * @static * @param promiseLike value that should be resolved * @returns */ static async fromPromise(promiseLike) { var result; try { var v = await promiseLike; result = Ok(v); } catch (e) { result = Err(e); } return result; } static fromAsync = this.fromPromise; /** * Compare `self` and another value. * * comparer algorithm (if comparatorFunction does not set): * - if self=Ok, other=Ok -> `self.unwrap() === other.unwrap()` * - if self=Err, other=Ok -> `false` * - if self=Ok, other=Err -> `false` * - if self=Err, other=Err -> `self.unwrapErr() === other.unwrapErr()` * You can pass your own function to compare * @example * const a = Ok(2) * const b = 2 * const same = a.equal(b, (self, another) => { * // self = Ok(2) * // another = 2 * return result.unwrap() === another * }) * console.log(same) // true * console.log(a.equal(b)) // false * console.log(a.equal(Ok(2))) // true * @param other another value * @param [comparatorFn=Object.is] compare function. Default - `Object.is` */ equal(other, comparatorFn = Object.is) { if (other instanceof _Result) { if (this.status === Status2.Ok && other.status === Status2.Ok) { return comparatorFn(this.value, other.value); } if (this.status === Status2.Err && other.status === Status2.Err) { return comparatorFn(this.error, other.error); } return false; } return comparatorFn(this, other); } valueOf() { if (this.isOk()) { return this.value; } return void 0; } toString() { return `${this.status}(${this.status === Status2.Err ? this.error : this.value})`; } /** * This internal method using for `JSON.stringify` seria