diff --git a/src/option.ts b/src/option.ts index ff30a81..9aaa323 100644 --- a/src/option.ts +++ b/src/option.ts @@ -1,7 +1,7 @@ import type { UnwrapErr, UnwrapOk } from "./result"; import { Result } from "./result"; -import { NONE, OPTION } from "./utils"; +import { NONE, SPECIES, SPECIES_OPTION } from "./utils"; type Falsy = false | 0 | 0n | "" | null | undefined; type Truthy = Exclude; @@ -72,7 +72,9 @@ export class Option { * @param maybeOption - A value that might be an `Option` */ public static isOption(maybeOption: unknown): maybeOption is Option { - return !!maybeOption && (maybeOption as Option)[OPTION] === 1; + return ( + !!maybeOption && (maybeOption as Option)[SPECIES] === SPECIES_OPTION + ); } /** @@ -87,7 +89,7 @@ export class Option { : false; } - private readonly [OPTION] = 1; + private readonly [SPECIES] = SPECIES_OPTION; private readonly _value: T; diff --git a/src/result.ts b/src/result.ts index 171ad0d..34f0bb5 100644 --- a/src/result.ts +++ b/src/result.ts @@ -1,7 +1,7 @@ import type { UnwrapOption } from "./option"; import { Option } from "./option"; -import { ERR, OK, RESULT } from "./utils"; +import { SPECIES, SPECIES_RESULT } from "./utils"; export type UnwrapOk = T extends Result ? U : Default; export type UnwrapErr = E extends Result @@ -21,15 +21,15 @@ export class Result { * @param value - A value of type `T` * @returns Wrap a value into an `Result`. */ - public static Ok = (value: T): Result => - Object.freeze(new Result(value, ERR)) as Result; + public static readonly Ok = (value: T): Result => + Object.freeze(new Result(value, true)) as Result; /** * @param error - An error of type `E` * @returns Wrap an error into an `Result`. */ - public static Err = (error: E): Result => - Object.freeze(new Result(OK, error)) as Result; + public static readonly Err = (error: E): Result => + Object.freeze(new Result(error, false)) as Result; /** * `Err` if the value is an `Error`. @@ -51,7 +51,6 @@ export class Result { predicate: (source: T | E) => source is T, thisArg?: any ): Result; - /** * `OK` if the value satisfies the predicate, otherwise `Err` * @@ -116,7 +115,9 @@ export class Result { public static isResult( maybeResult: unknown ): maybeResult is Result { - return !!maybeResult && (maybeResult as Result)[RESULT] === 1; + return ( + !!maybeResult && (maybeResult as Result)[SPECIES] === SPECIES_RESULT + ); } /** @@ -127,20 +128,18 @@ export class Result { */ public static isSame(a: unknown, b: unknown): boolean { return Result.isResult(a) && Result.isResult(b) - ? a.isOk() - ? Object.is(a._value, b._value) - : Object.is(a._error, b._error) + ? Object.is(a._value, b._value) : false; } - private [RESULT] = 1; + private readonly [SPECIES] = SPECIES_RESULT; - private _value: T; - private _error: E; + private readonly _value: T | E; + private readonly _ok: boolean; - private constructor(value: T, error: E) { + private constructor(value: T | E, ok: boolean) { this._value = value; - this._error = error; + this._ok = ok; } /** @@ -149,8 +148,8 @@ export class Result { * The iterator yields one value if the result is `Ok`, otherwise none. */ *[Symbol.iterator]() { - if (this.isOk()) { - yield this._value; + if (this._ok) { + yield this._value as T; } } @@ -158,14 +157,14 @@ export class Result { * @returns `true` if the `Result` is an `Ok`. */ public isOk(): boolean { - return this._value !== OK; + return this._ok; } /** * @returns `true` if the `Result` is an `Err`. */ public isErr(): boolean { - return this._error !== ERR; + return !this._ok; } /** @@ -173,7 +172,7 @@ export class Result { * @param thisArg - If provided, it will be used as the this value for each invocation of predicate. If it is not provided, `undefined` is used instead. */ public isOkAnd(predicate: (value: T) => boolean, thisArg?: any): boolean { - return this.isOk() && predicate.call(thisArg, this._value); + return this._ok && predicate.call(thisArg, this._value as T); } /** @@ -181,7 +180,7 @@ export class Result { * @param thisArg - If provided, it will be used as the this value for each invocation of predicate. If it is not provided, `undefined` is used instead. */ public isErrAnd(predicate: (error: E) => boolean, thisArg?: any): boolean { - return this.isErr() && predicate.call(thisArg, this._error); + return !this._ok && predicate.call(thisArg, this._value as E); } /** @@ -192,14 +191,12 @@ export class Result { */ public isSame(other: unknown): boolean { return Result.isResult(other) - ? this.isOk() - ? Object.is(this._value, other._value) - : Object.is(this._error, other._error) + ? Object.is(this._value, other._value) : false; } public and(resultB: Result): Result { - return this.isOk() ? resultB : (this as Err); + return this._ok ? resultB : (this as Err); } /** @@ -212,8 +209,8 @@ export class Result { getResultB: (value: T) => Result, thisArg?: any ): Result { - return this.isOk() - ? getResultB.call(thisArg, this._value) + return this._ok + ? getResultB.call(thisArg, this._value as T) : (this as Err); } @@ -225,7 +222,7 @@ export class Result { * @param resultB - A `Result` */ public or(resultB: Result): Result { - return this.isOk() ? this : resultB; + return this._ok ? this : resultB; } /** @@ -238,15 +235,14 @@ export class Result { getResultB: () => Result, thisArg?: any ): Result { - return this.isOk() ? this : getResultB.call(thisArg); + return this._ok ? this : getResultB.call(thisArg); } /** * Converts from `Option>` to `Option` */ public flatten(): Result, E | UnwrapErr> { - return this.isOk() && - Result.isResult, UnwrapErr>(this._value) + return this._ok && Result.isResult, UnwrapErr>(this._value) ? this._value : (this as Result, E | UnwrapErr>); } @@ -259,8 +255,8 @@ export class Result { * @returns `Err` if the `Result` is `Err`, otherwise returns `Ok(fn(value))`. */ public map(fn: (value: T) => U, thisArg?: any): Result { - return this.isOk() - ? Result.Ok(fn.call(thisArg, this._value)) + return this._ok + ? Result.Ok(fn.call(thisArg, this._value as T)) : (this as Err); } @@ -274,9 +270,9 @@ export class Result { * @returns `Ok` if the `Result` is `Ok`, otherwise returns `Err(fn(error))`. */ public mapErr(fn: (error: E) => U, thisArg?: any): Result { - return this.isErr() - ? Result.Err(fn.call(thisArg, this._error)) - : (this as Ok); + return this._ok + ? (this as Ok) + : Result.Err(fn.call(thisArg, this._value as E)); } /** @@ -287,7 +283,7 @@ export class Result { * - `Ok(_)` will be mapped to `Some(Ok(_))`. */ public transpose(): Option, E>> { - return this.isOk() + return this._ok ? Option.isOption>(this._value) ? this._value.map(Result.Ok) : Option.Some(this) @@ -298,14 +294,14 @@ export class Result { * Converts from `Result` to `Option` and discarding the error, if any. */ public ok(): Option { - return this.isOk() ? Option.Some(this._value) : Option.None; + return this._ok ? Option.Some(this._value) : Option.None; } /** * Converts from `Result` to `Option` and discarding the value, if any. */ public err(): Option { - return this.isErr() ? Option.Some(this._error) : Option.None; + return this._ok ? Option.None : Option.Some(this._value); } /** @@ -316,8 +312,8 @@ export class Result { * @param message - Optional Error message */ public unwrap(message = "called `Result.unwrap()` on an `Err`"): T { - if (this.isOk()) { - return this._value; + if (this._ok) { + return this._value as T; } throw new Error(message); } @@ -335,7 +331,7 @@ export class Result { */ public unwrapOr(defaultValue: U): T | U; public unwrapOr(defaultValue?: T): T | undefined { - return this.isOk() ? this._value : defaultValue; + return this._ok ? (this._value as T) : defaultValue; } /** @@ -344,7 +340,7 @@ export class Result { * @param thisArg - If provided, it will be used as the this value for each invocation of predicate. If it is not provided, `undefined` is used instead. */ public unwrapOrElse(fn: () => U, thisArg?: any): T | U { - return this.isOk() ? this._value : fn.call(thisArg); + return this._ok ? (this._value as T) : fn.call(thisArg); } /** @@ -357,10 +353,10 @@ export class Result { public unwrapErr( message = "called `Result.unwrapErr()` on an `Ok` value" ): E { - if (this.isErr()) { - return this._error; + if (this._ok) { + throw new Error(message); } - throw new Error(message); + return this._value as E; } /** @@ -376,7 +372,7 @@ export class Result { */ public unwrapErrOr(defaultError: U): E | U; public unwrapErrOr(defaultError?: E): E | undefined { - return this.isErr() ? this._error : defaultError; + return this._ok ? defaultError : (this._value as E); } /** @@ -385,7 +381,7 @@ export class Result { * @param thisArg - If provided, it will be used as the this value for each invocation of predicate. If it is not provided, `undefined` is used instead. */ public unwrapErrOrElse(fn: () => U, thisArg?: any): E | U { - return this.isErr() ? this._error : fn.call(thisArg); + return this._ok ? fn.call(thisArg) : (this._value as E); } /** @@ -396,11 +392,11 @@ export class Result { * @returns The value returned by the provided function. */ public match(Ok: (value: T) => U, Err: (error: E) => U): U { - return this.isOk() ? Ok(this._value) : Err(this._error); + return this._ok ? Ok(this._value as T) : Err(this._value as E); } public toString(): string { - return this.isOk() ? `Ok(${this._value})` : `Err(${this._error})`; + return `${this._ok ? "Ok" : "Err"}(${this._value})`; } } diff --git a/src/utils.ts b/src/utils.ts index 55a9e3a..8903b20 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,9 +1,9 @@ /** A meaningless value */ -export const NONE: any = /* @__PURE__ */ Symbol.for("$NONE$\u2009"); -export const OK: any = /* @__PURE__ */ Symbol.for("$OK$\u2009"); -export const ERR: any = /* @__PURE__ */ Symbol.for("$ERR$\u2009"); -export const OPTION = /* @__PURE__ */ Symbol.for("$OPTION$\u2009"); -export const RESULT = /* @__PURE__ */ Symbol.for("$RESULT$\u2009"); +export const NONE = /* @__PURE__ */ Symbol.for("$NONE$\u2009"); +export const SPECIES = /* @__PURE__ */ Symbol.for("$TSUR_SPECIES$\u2009"); + +export const SPECIES_OPTION = 1; +export const SPECIES_RESULT = 2; export const positiveNumber = (index: number): boolean => index >= 0;