@rslike/std
Version:
JavaScript Standard library without udndefined behavior!
1,604 lines (1,602 loc) • 56.2 kB
JavaScript
// 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