@ts-rust/std
Version:
Rust-inspired utilities for TypeScript: Option, Result, and error handling for safer, more predictable code.
1,351 lines (1,330 loc) • 83.7 kB
JavaScript
/******************************************************************************
Copyright (c) Microsoft Corporation.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
***************************************************************************** */
/* global Reflect, Promise, SuppressedError, Symbol, Iterator */
function __classPrivateFieldGet$1(receiver, state, kind, f) {
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
}
function __classPrivateFieldSet$1(receiver, state, value, kind, f) {
if (typeof state === "function" ? receiver !== state || true : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
return (state.set(receiver, value)), value;
}
typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
var e = new Error(message);
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
};
/**
* Creates a constant function that always returns the same value.
*
* Inspired by functional programming (e.g., Haskell’s `const`), this utility
* takes a value and returns a function that, when called, always returns that
* original value. The returned function **accepts any number of arguments**,
* but ignores them. This is useful for predictable behavior in higher-order
* functions, like defaults or stubs.
*
* @example
* ```ts
* const always42 = cnst(42);
* const alwaysHello = cnst("hello");
*
* expect(always42()).toBe(42);
* expect(always42("ignored")).toBe(42); // Arguments are ignored
* expect(always42(1, 2, 3)).toBe(42); // Still returns 42
* expect(alwaysHello()).toBe("hello");
*
* const mapWithDefault = [1, 2, 3].map(cnst(0));
* expect(mapWithDefault).toEqual([0, 0, 0]);
* ```
*
* @param value - The constant value to be returned by the function.
* @returns A function that always returns `value`, ignoring any arguments.
*/
function cnst(value) {
return () => value;
}
var __Left_value, __Right_value;
/**
* Creates an {@link Either} with a "left" value of type `T`.
*
* Use this function to construct a {@link Left} variant, typically representing an error or
* alternative outcome.
*
* @example
* ```ts
* const e = left<string, number>("error");
* expect(e.isLeft()).toBe(true);
* expect(e.left).toBe("error");
* ```
*/
function left(value) {
return new _Left(value);
}
/**
* Creates an {@link Either} with a "right" value of type `U`.
*
* Use this function to construct a {@link Right} variant, typically representing a successful
* or primary outcome.
*
* @example
* ```ts
* const e = right<string, number>(42);
* expect(e.isRight()).toBe(true);
* expect(e.right).toBe(42);
* ```
*/
function right(value) {
return new _Right(value);
}
class _Left {
get() {
return __classPrivateFieldGet$1(this, __Left_value, "f");
}
get left() {
return __classPrivateFieldGet$1(this, __Left_value, "f");
}
constructor(value) {
__Left_value.set(this, void 0);
__classPrivateFieldSet$1(this, __Left_value, value);
}
isLeft() {
return true;
}
isRight() {
return false;
}
either(f, _g) {
return f(this.left);
}
}
__Left_value = new WeakMap();
class _Right {
get() {
return __classPrivateFieldGet$1(this, __Right_value, "f");
}
get right() {
return __classPrivateFieldGet$1(this, __Right_value, "f");
}
constructor(value) {
__Right_value.set(this, void 0);
__classPrivateFieldSet$1(this, __Right_value, value);
}
isLeft() {
return false;
}
isRight() {
return true;
}
either(_f, g) {
return g(this.right);
}
}
__Right_value = new WeakMap();
/**
* Checks if a value is a `Promise`, narrowing its type to `Promise<unknown>`.
*
* This type guard determines whether the input is an instance of the native
* `Promise` class, indicating it is a standard JavaScript promise.
*
* @example
* ```ts
* const x = Promise.resolve(42);
* const y = new Promise((resolve) => resolve("hello"));
* const z = { then: () => {} }; // Promise-like but not a Promise
*
* expect(isPromise(x)).toBe(true);
* expect(isPromise(y)).toBe(true);
* expect(isPromise(z)).toBe(false);
*
* if (isPromise(x)) {
* expect(await x).toBe(42); // Type narrowed to Promise<unknown>
* }
* ```
*/
function isPromise(x) {
return x instanceof Promise;
}
/**
* Converts `Promise`, `PromiseLike` or an actual value into a `Promise`.
*
* This utility function normalizes its input by returning the input directly if it is
* already a `Promise`, or wrapping it in a resolved `Promise` if it is not.
* It ensures that the result is always a `Promise`, regardless of whether the
* input is synchronous or asynchronous.
*
* @example
* ```ts
* const syncValue = 42;
* const asyncValue = Promise.resolve("hello");
*
* const syncPromise = toPromise(syncValue);
* const asyncPromise = toPromise(asyncValue);
*
* expect(syncPromise).toBeInstanceOf(Promise);
* expect(await syncPromise).toBe(42);
*
* expect(asyncPromise).toBe(asyncValue); // Same Promise instance
* expect(await asyncPromise).toBe("hello");
* ```
*/
const toPromise = (x) => Promise.resolve(x);
/**
* Creates a deep clone of an {@link Error}, duplicating its message, nested
* causes and reasons.
*
* This function constructs a new {@link Error} instance with the same message as the
* provided error, and recursively clones any nested `reason` or `cause` if they are
* errors. Non-error `cause` values are cloned if possible, preserving the error’s
* structure without shared references.
*/
const cloneError = (err) => {
const clone = new Error(err.message);
if (err.stack) {
Object.defineProperty(clone, "stack", {
value: err.stack,
writable: true,
configurable: true,
});
}
if ("reason" in err && err.reason instanceof Error) {
Object.defineProperty(clone, "reason", {
value: cloneError(err.reason),
writable: true,
configurable: true,
});
}
if ("cause" in err) {
try {
Object.defineProperty(clone, "cause", {
value: err.cause instanceof Error
? cloneError(err.cause)
: structuredClone(err.cause),
writable: true,
configurable: true,
});
}
catch {
// do not care about the error
}
}
return clone;
};
/**
* Converts a value to a human-readable string representation.
*
* This utility function provides a robust way to stringify various types,
* including primitives, objects, functions, symbols, promises, and more.
* It ensures meaningful representations, preventing errors with circular
* structures or unstringifiable objects.
*
* - `null` and `undefined` are returned as `"null"` and `"undefined"`, respectively.
* - `string`, `number`, `boolean`, `bigint`, and `symbol` values are converted to strings.
* - Functions return their name (or `"anonymous"` if unnamed).
* - Promises return `"promise"`.
* - Objects attempt `toString()`, falling back to `JSON.stringify()`.
* - If an object cannot be stringified (e.g., circular references), it returns `"[Circular or Unstringifiable Object]"`.
*
* If `quoteString` is `true`, string values are wrapped in single quotes.
*
* @example
* ```ts
* expect(stringify(42)).toBe("42");
* expect(stringify("hello")).toBe("hello");
* expect(stringify("hello", true)).toBe("'hello'");
* expect(stringify(null)).toBe("null");
* expect(stringify(undefined)).toBe("undefined");
* expect(stringify(Symbol("id"))).toBe("Symbol(id)");
* expect(stringify(BigInt(1234))).toBe("1234n");
* expect(stringify(() => {})).toBe("[Function: anonymous]");
* expect(stringify({ a: 1 })).toBe('{"a":1}');
* expect(stringify(Promise.resolve())).toBe("promise");
* ```
*
* @param value - The value to stringify.
* @param quoteString - Whether to wrap string values in single quotes (default to `false`).
* @returns A string representation of the value.
*/
function stringify(value, quoteString = false) {
if (value instanceof Promise) {
return "promise";
}
if (value === null) {
return "null";
}
if (value === undefined) {
return "undefined";
}
if (typeof value === "string") {
return quoteString ? `'${value}'` : value;
}
if (typeof value === "number" || typeof value === "boolean") {
return String(value);
}
if (typeof value === "symbol") {
return value.toString();
}
if (typeof value === "bigint") {
return value.toString() + "n";
}
if (typeof value === "function") {
return `[Function: ${value.name || "anonymous"}]`;
}
if (typeof value === "object") {
const v = value;
if (typeof v.toString === "function" &&
v.toString !== Object.prototype.toString) {
return v.toString();
}
try {
return JSON.stringify(value);
}
catch {
return "[Circular or Unstringifiable Object]";
}
}
return "[Unknown Type]";
}
/**
* A generic error class extending `Error` with a typed `kind` and optional `reason`.
*
* This class provides a structured way to represent errors with a category (`kind`) of a
* primitive type (e.g., string, number, enum) and an optional underlying cause (`reason`).
* The error message is automatically formatted to include both the `kind` and `reason`
* (if provided), making it suitable for categorized error handling in libraries or
* applications.
*
* @template T - The type of the error `kind`, constrained to {@link Primitive} (e.g., string, number, enum).
*
* @example
* ```ts
* const err1 = new AnyError("Invalid input", "ValidationError");
* const err2 = new AnyError("File not found", 404, new Error("ENOENT"));
*
* expect(err1.message).toBe("[ValidationError] Invalid input.");
* expect(err2.message).toBe("[404] File not found. Reason: ENOENT");
* expect(err2.kind).toBe(404);
* expect(err2.reason.message).toBe("ENOENT");
* ```
*/
class AnyError extends Error {
/**
* Constructs a new {@link AnyError} instance with a message, kind,
* and optional reason.
*
* The error’s message is formatted as `[kind] message` or `[kind] message. Reason: reason`
* if a `reason` is provided. The `name` is set to the constructor’s name,
* and the `reason` is normalized to an `Error` instance.
*
* @param message - The descriptive message for the error.
* @param kind - The category or type of the error, a primitive value.
* @param reason - An optional underlying cause, which can be any value (converted to `Error` if not already).
*/
constructor(message, kind, reason) {
super(message);
this.name = this.constructor.name;
this.kind = kind;
this.reason = mkReason(arguments.length === 3 ? reason : kind);
this.message = mkMessage(message, kind, reason);
}
}
const mkMessage = (msg, kind, reason) => {
let message = `[${stringify(kind)}] ${msg}.`;
if (reason !== undefined) {
message += ` Reason: ${stringify(reason)}`;
}
return message;
};
const mkReason = (reason) => reason instanceof Error ? reason : new Error(stringify(reason));
/**
* Enumerates error codes specific to {@link Option} operations.
*
* These codes are used in {@link OptionError} instances thrown by methods like
* {@link Optional.unwrap | unwrap} or {@link Optional.expect | expect} when operations
* fail due to the state of the option.
*/
var OptionErrorKind;
(function (OptionErrorKind) {
OptionErrorKind["ValueAccessedOnNone"] = "ValueAccessedOnNone";
OptionErrorKind["ExpectCalledOnNone"] = "ExpectCalledOnNone";
OptionErrorKind["UnwrapCalledOnNone"] = "UnwrapCalledOnNone";
OptionErrorKind["PredicateException"] = "PredicateException";
})(OptionErrorKind || (OptionErrorKind = {}));
/**
* Checks if a value is an {@link OptionError}, narrowing its type if true.
*
* @param e - The value to check.
* @returns `true` if the value is a {@link OptionError}, narrowing to `OptionError`.
*/
function isOptionError(e) {
return e instanceof OptionError;
}
/**
* An error thrown by {@link Option} methods when operations fail due to the option's state
* or unexpected conditions.
*
* This class extends {@link AnyError} with error kinds specific to {@link Option}
* operations, as defined in {@link OptionErrorKind}. It is typically thrown by methods like
* {@link Optional.unwrap | unwrap}, {@link Optional.expect | expect}, or others that enforce
* strict access or behavior on {@link Some} or {@link None} variants. Use it to handle
* failures gracefully in a type-safe manner, inspecting the {@link OptionErrorKind} to
* determine the cause.
*
* @example
* ```ts
* const opt = none<number>();
* try {
* opt.unwrap();
* } catch (e) {
* expect(e).toBeInstanceOf(OptionError);
* expect(e.kind).toBe(OptionErrorKind.UnwrapCalledOnNone);
* expect(e.message).toBe("`unwrap`: called on `None`");
* }
* ```
*/
class OptionError extends AnyError {
}
/******************************************************************************
Copyright (c) Microsoft Corporation.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
***************************************************************************** */
/* global Reflect, Promise, SuppressedError, Symbol, Iterator */
function __classPrivateFieldGet(receiver, state, kind, f) {
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
}
function __classPrivateFieldSet(receiver, state, value, kind, f) {
if (kind === "m") throw new TypeError("Private method is not writable");
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
}
typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
var e = new Error(message);
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
};
var _CheckedFailure_error;
/* eslint-enable @typescript-eslint/no-unused-vars */
/**
* Enumerates error codes specific to {@link Result} operations.
*
* These codes categorize failures in {@link ResultError} instances thrown by methods
* such as {@link Resultant.unwrap | unwrap} or {@link Resultant.expect | expect}
* when the result’s state (e.g., {@link Ok} or {@link Err}) doesn’t match the
* operation’s expectations.
*/
var ResultErrorKind;
(function (ResultErrorKind) {
ResultErrorKind["ErrorAccessedOnOk"] = "ErrorAccessedOnOk";
ResultErrorKind["ValueAccessedOnErr"] = "ValueAccessedOnErr";
ResultErrorKind["ExpectCalledOnErr"] = "ExpectCalledOnErr";
ResultErrorKind["ExpectErrCalledOnOk"] = "ExpectErrCalledOnOk";
ResultErrorKind["UnwrapCalledOnErr"] = "UnwrapCalledOnErr";
ResultErrorKind["UnwrapErrCalledOnOk"] = "UnwrapErrCalledOnOk";
ResultErrorKind["FlattenCalledOnFlatResult"] = "FlattenCalledOnFlatResult";
ResultErrorKind["ResultRejection"] = "ResultRejection";
ResultErrorKind["PredicateException"] = "PredicateException";
ResultErrorKind["FromOptionException"] = "FromOptionException";
ResultErrorKind["Unexpected"] = "Unexpected";
})(ResultErrorKind || (ResultErrorKind = {}));
/**
* An error class for {@link Result} operations, extending {@link AnyError} with
* specific {@link ResultErrorKind} codes.
*
* This class represents failures tied to {@link Result} methods, such as accessing
* a value from an {@link Err} or an error from an {@link Ok}. It provides a structured
* way to handle such failures by embedding a {@link ResultErrorKind} and an optional
* `reason` for additional context.
*
* @example
* ```ts
* const res = err<number, string>("failure");
* try {
* res.unwrap();
* } catch (e) {
* if (isResultError(e)) {
* console.log(e.kind); // "UnwrapCalledOnErr"
* console.log(e.message); // "[UnwrapCalledOnErr] `unwrap`: called on `Err`."
* }
* }
* ```
*/
class ResultError extends AnyError {
/**
* Creates a deep clone of this {@link ResultError}, duplicating all properties
* and ensuring no shared references.
*
* This method constructs a new {@link ResultError} instance with the same `kind`
* and a cloned `reason`. Since `kind` is a {@link Primitive}, it is copied as-is,
* while `reason` (an `Error`) is recreated with its `message` and, if available,
* its `stack` or `cause`. The `message` and `name` are regenerated to match the
* original formatting, and the `stack` trace is set to the new instance’s call
* context (though it may be copied if supported).
*
* @returns A new deeply cloned {@link ResultError} instance.
*/
clone() {
const c = new ResultError(this.message, this.kind, cloneError(this.reason));
c.message = this.message;
return c;
}
}
/**
* Checks if a value is a {@link ResultError}, narrowing its type if true.
*
* @param e - The value to check.
* @returns `true` if the value is a {@link ResultError}, narrowing to `ResultError`.
*/
function isResultError(e) {
return e instanceof ResultError;
}
/**
* Creates a {@link CheckedError} representing an expected error of type `E`.
*
* Use this function to construct an error for anticipated failures, such as
* validation errors or known conditions.
*
* @template E - The type of the expected error.
* @param error - The expected error value to encapsulate.
* @returns A {@link CheckedError} containing the expected error.
*/
function expectedError(error) {
return CheckedFailure.expected(error);
}
function unexpectedError(error, kind, reason) {
if (typeof error === "string") {
const errorKind = kind ?? ResultErrorKind.Unexpected;
return CheckedFailure.unexpectedFromArgs(error, errorKind, reason);
}
return CheckedFailure.unexpected(error);
}
/**
* Checks if a value is a {@link CheckedError}, narrowing its type if true.
*
* @param e - The value to check.
* @returns `true` if the value is a {@link CheckedError}, narrowing to
* `CheckedError<unknown>`.
*/
function isCheckedError(e) {
return e instanceof CheckedFailure;
}
/**
* Internal implementation of {@link CheckedError}.
*
* This class encapsulates the error state of a {@link Result}, holding either an
* expected error of type `E` or an unexpected {@link ResultError}. It is not
* intended for direct use; prefer the {@link expectedError} and {@link unexpectedError}
* factory functions.
*/
class CheckedFailure extends Error {
static expected(error) {
return new CheckedFailure(right(error));
}
static unexpected(error) {
return new CheckedFailure(left(error));
}
static unexpectedFromArgs(error, kind, reason) {
return new CheckedFailure(left(new ResultError(error, kind, reason)));
}
get expected() {
return __classPrivateFieldGet(this, _CheckedFailure_error, "f").isRight() ? __classPrivateFieldGet(this, _CheckedFailure_error, "f").right : undefined;
}
get unexpected() {
return __classPrivateFieldGet(this, _CheckedFailure_error, "f").isLeft() ? __classPrivateFieldGet(this, _CheckedFailure_error, "f").left : undefined;
}
constructor(error) {
super(`${error.isRight() ? "Expected" : "Unexpected"} error occurred: ${stringify(error.get())}`);
_CheckedFailure_error.set(this, void 0);
this.name = this.constructor.name;
__classPrivateFieldSet(this, _CheckedFailure_error, error, "f");
}
get() {
return __classPrivateFieldGet(this, _CheckedFailure_error, "f").get();
}
handle(f, g) {
return __classPrivateFieldGet(this, _CheckedFailure_error, "f").either(f, g);
}
isExpected() {
return __classPrivateFieldGet(this, _CheckedFailure_error, "f").isRight();
}
isUnexpected() {
return __classPrivateFieldGet(this, _CheckedFailure_error, "f").isLeft();
}
toString() {
return this.handle((re) => stringify(re, true), (e) => stringify(e, true));
}
}
_CheckedFailure_error = new WeakMap();
/**
* Checks if a value is a {@link Primitive}, narrowing its type accordingly.
*
* This type guard determines whether the input is a **primitive** value,
* meaning it is one of the fundamental, immutable JavaScript types:
* - `boolean`
* - `string`
* - `number`
* - `bigint`
* - `symbol`
* - `null`
* - `undefined`
*
* Unlike objects, arrays, or functions, primitives are **copied by value** and
* do not have reference-based mutations.
*
* @example
* ```ts
* expect(isPrimitive(42)).toBe(true);
* expect(isPrimitive("hello")).toBe(true);
* expect(isPrimitive(null)).toBe(true);
* expect(isPrimitive({})).toBe(false);
* expect(isPrimitive([])).toBe(false);
*
* function processValue(x: unknown) {
* if (isPrimitive(x)) {
* // x is now narrowed to Primitive
* }
* }
* ```
*/
function isPrimitive(x) {
return (x === null ||
x === undefined ||
typeof x === "boolean" ||
typeof x === "string" ||
typeof x === "number" ||
typeof x === "bigint" ||
typeof x === "symbol");
}
var __Result_state, __PendingResult_promise;
function ok(value) {
return _Result.ok(value);
}
function err(error) {
return _Result.error(error);
}
/**
* Executes a synchronous action and wraps the outcome in a {@link Result},
* handling errors with a custom error mapper.
*
* The {@link run} function attempts to execute the provided `action` function,
* which returns a value of type `T`. If the action succeeds, it returns an
* {@link Ok} variant containing the result. If the action fails (throws an error),
* the error is passed to the `mkErr` function to create an error of type `E`,
* which is then wrapped in an {@link Err} variant.
*
* This function is useful for safely executing operations that might fail,
* ensuring errors are handled in a type-safe way using the {@link Result} type.
*
* @param action - A function that performs the operation, returning a value of type `T`.
* @param mkErr - A function that converts an error (of type `unknown`) into an error of type `E`.
* @returns A `Result<T, E>` containing either the successful result (`Ok<T>`) or the mapped error (`Err<E>`).
*
* @example
* ```ts
* import { run, Result } from "@ts-rust/std";
*
* const result: Result<{ key: string }, Error> = run(
* (): { key: string } => JSON.parse('{ key: "value" }'),
* (e) => new Error(`Operation failed: ${JSON.stringify(e)}`),
* );
* if (result.isOk()) {
* console.log(result.unwrap()); // { key: "value" }
* }
* ```
*/
function run(action, mkErr) {
const errorResult = (error) => {
try {
const handledError = mkErr(error);
return err(handledError);
}
catch (e) {
return err(unexpectedError("`run`: callback `mkErr` threw an exception", ResultErrorKind.PredicateException, e));
}
};
try {
return ok(action());
}
catch (error) {
return errorResult(error);
}
}
/**
* Executes an asynchronous action and wraps the outcome in a {@link PendingResult},
* handling errors with a custom error mapper.
*
* The {@link runAsync} function attempts to execute the provided `action` function,
* which returns a value of type `Promise<T>`. If the action succeeds, it returns a
* {@link PendingResult} that resolves to {@link Ok} variant containing the value.
* If the action fails (throws an error), the error is passed to the `mkErr` function
* to create an error of type `E`, which is then wrapped in an {@link Err} variant.
*
* This function is useful for safely executing operations that might fail,
* ensuring errors are handled in a type-safe way using the {@link Result} type.
*
* @param action - A function that performs the operation, returning a `Promise` resolving to `T`.
* @param mkErr - A function that converts an error (of type `unknown`) into an error of type `E`.
* @returns A `PendingResult<T, E>` that resolves to either a value (`Ok<T>`) or the mapped error (`Err<E>`).
*
* @example
* ```ts
* import { run, PendingResult, Result } from "@ts-rust/std";
*
* const pendingRes: PendingResult<string, Error> = runAsync(
* (): Promise<string> => fetch("https://api.example.com/text").then(res => res.text()),
* (e) => new Error(`Fetch failed: ${JSON.stringify(e)}`),
* );
*
* const res: Result<string, Error> = await pendingRes;
*
* if (res.isErr()) {
* console.log(res.unwrapErr().message); // Fetch failed: ...
* }
* ```
*/
function runAsync(action, mkErr) {
const errorResult = (error) => {
try {
return pendingErr(mkErr(error));
}
catch (e) {
return pendingErr(unexpectedError("`runAsync`: callback `mkErr` threw an exception", ResultErrorKind.PredicateException, e));
}
};
try {
return pendingOk(action());
}
catch (error) {
return errorResult(error);
}
}
/**
* Safely executes an action that returns a {@link Result}, capturing thrown
* synchronous errors as an {@link Err} variant.
*
* The {@link runResult} function executes the provided `resultAction` function,
* which returns a `Result<T, E>`. If the action succeeds, it returns the {@link Result}
* as-is (either `Ok<T>` or `Err<E>`). If the action throws an error, it is
* captured and wrapped in an {@link Err} variant returning {@link UnexpectedError} with a
* `ResultErrorKind.Unexpected` kind.
*
* This function is useful for safely running synchronous `Result`-producing actions,
* if you are not 100% sure that the action will not throw an error, ensuring that any
* thrown errors are converted into an {@link Err} variant in a type-safe way.
*
* @param getResult - A function that returns a `Result<T, E>`.
* @returns A `Result<T, E>` containing either the original `Result` from `resultAction` or an `Err<E>` if the action throws an error.
*
* @example
* ```ts
* import { runResult, ok, err } from "@ts-rust/std";
*
* // Successful Result
* const success = runResult(() => ok(42));
* console.log(success.unwrap()); // 42
*
* // Failed Result
* const failure = runResult(() => err(new Error("Already failed")));
* // "Expected error occurred: Error: Already failed"
* console.log(failure.unwrapErr().expected?.message);
*
* // Action throws an error
* const thrown = runResult(() => { throw new Error("Oops"); });
* // "Unexpected error occurred: ResultError: [Unexpected] `runResult`: result action threw an exception. Reason: Error: Oops"
* console.log(thrown.unwrapErr().unexpected?.message);
* ```
*/
function runResult(getResult) {
try {
return getResult();
}
catch (e) {
return err(unexpectedError("`runResult`: result action threw an exception", ResultErrorKind.Unexpected, e));
}
}
/**
* Safely executes an action that returns a {@link PendingResult}, capturing
* thrown synchronous errors as an {@link Err} variant.
*
* The {@link runPendingResult} function executes the provided `resultAction`
* function, which returns a `PendingResult<T, E>`. If the action succeeds, it
* returns the {@link PendingResult} as-is. If the action throws an error synchronously,
* the error is captured and wrapped in a resolved {@link Err} variant returning
* {@link UnexpectedError} with a `ResultErrorKind.Unexpected` kind.
*
* This overload is useful for safely running asynchronous `PendingResult`-producing actions,
* if you are not 100% sure that the action will not throw an error, ensuring that any
* synchronous errors are converted into an {@link Err} variant in a type-safe way.
*
* @param getResult - A function that returns a `Result<T, E>`, `PendingResult<T, E>` or a `Promise<Result<T, E>>`.
* @returns A `PendingResult<T, E>` containing either the original `PendingResult` from `resultAction` or a resolved `Promise` with an `Err<E>` if the action throws synchronously.
*
* @example
* ```ts
* import { runPendingResult, pendingOk, pendingErr } from "@ts-rust/std";
*
* // Successful Result
* const success = await runPendingResult(() => pendingOk(42));
* console.log(success.unwrap()); // 42
*
* // Failed Result
* const failure = await runPendingResult(() => pendingErr(new Error("Already failed")));
* // "Expected error occurred: Error: Already failed"
* console.log(failure.unwrapErr().expected?.message);
*
* // Action throws an error
* const thrown = await runPendingResult(() => { throw new Error("Oops"); });
* // "Unexpected error occurred: ResultError: [Unexpected] `runPendingResult`: result action threw an exception. Reason: Error: Oops"
* console.log(thrown.unwrapErr().unexpected?.message);
* ```
*/
function runPendingResult(getResult) {
try {
const result = getResult();
if (isResult(result)) {
return result.toPending();
}
if (isPendingResult(result)) {
return result;
}
return pendingResult(result);
}
catch (e) {
return pendingErr(unexpectedError("`runPendingResult`: result action threw an exception", ResultErrorKind.Unexpected, e));
}
}
/**
* Creates a {@link PendingResult | PendingResult\<T, E>} that resolves to
* {@link Ok} containing the awaited value.
*
* Takes a value or promise and wraps its resolved result in an {@link Ok},
* ensuring the value type is `Awaited` to handle any `PromiseLike` input.
*
* @template T - The type of the input value or promise.
* @template E - The type of the potential error.
* @param value - The value or promise to wrap in {@link Ok}.
* @returns A {@link PendingResult} resolving to {@link Ok} with the awaited value.
*
* @example
* ```ts
* const x = pendingOk<number, string>(42);
* const y = pendingOk<string, number>(Promise.resolve("hello"));
*
* expect(await x).toStrictEqual(ok(42));
* expect(await y).toStrictEqual(ok("hello"));
* ```
*/
function pendingOk(value) {
return _PendingResult.create(toPromise(value).then((x) => ok(x)));
}
/**
* Creates a {@link PendingResult | PendingResult\<T, E>} that resolves to
* {@link Err} containing the awaited error.
*
* Takes an error or promise and wraps its resolved result in an {@link Err},
* ensuring the error type is `Awaited` to handle any `PromiseLike` input.
*
* @template T - The type of the potential value.
* @template E - The type of the input error or promise.
* @param error - The error or promise to wrap in {@link Err}.
* @returns A {@link PendingResult} resolving to {@link Err} with the awaited error.
*
* @example
* ```ts
* const x = pendingErr<number, string>("failure");
* const y = pendingErr<string, number>(Promise.resolve(42));
*
* expect(await x).toStrictEqual(err("failure"));
* expect(await y).toStrictEqual(err(42));
* ```
*/
function pendingErr(error) {
return _PendingResult.create(settleResult(toPromise(error).then((e) => err(e))));
}
/**
* Creates a {@link PendingResult | PendingResult\<T, E>} from a result,
* promise, or factory function.
*
* Accepts a {@link Result}, a `Promise` resolving to a {@link Result}, or
* a function returning either, and converts it into a pending result, handling
* asynchronous resolution as needed.
*
* @template T - The type of the value in the result.
* @template E - The type of the expected error in the result.
* @param resultOrFactory - The {@link Result}, promise, or factory function producing a {@link Result}.
* @returns A {@link PendingResult} resolving to the provided or produced result.
*
* @example
* ```ts
* const x = pendingResult(ok<number, string>(42));
* const y = pendingResult(() => Promise.resolve(err<string, number>(42)));
* const z = pendingResult(async () => err<string, boolean>(true));
*
* expect(await x).toStrictEqual(ok(42));
* expect(await y).toStrictEqual(err(42));
* expect(await z).toStrictEqual(err(true));
* ```
*/
function pendingResult(resultOrFactory) {
if (typeof resultOrFactory === "function") {
return pendingResult(resultOrFactory());
}
return _PendingResult.create(resultOrFactory);
}
/**
* Checks if a value is a {@link Result}, narrowing its type to
* `Result<unknown, unknown>`.
*
* This type guard verifies whether the input conforms to the {@link Result}
* interface, indicating it is either an {@link Ok} or {@link Err}.
*
* @param x - The value to check.
* @returns `true` if the value is a {@link Result}, narrowing to `Result<unknown, unknown>`.
*
* @example
* ```ts
* const x: unknown = ok<number, string>(42);
* const y: unknown = err<number, string>("failure");
* const z: unknown = "not a result";
*
* expect(isResult(x)).toBe(true);
* expect(isResult(y)).toBe(true);
* expect(isResult(z)).toBe(false);
*
* if (isResult(x)) {
* expect(x.isOk()).toBe(true); // Type narrowed to Result<unknown, unknown>
* }
* ```
*/
function isResult(x) {
return x instanceof _Result;
}
/**
* Checks if a value is a {@link PendingResult}, narrowing its type to
* `PendingResult<unknown, unknown>`.
*
* This type guard verifies whether the input is a {@link PendingResult},
* indicating it wraps a `Promise` resolving to a {@link Result}
* (either {@link Ok} or {@link Err}).
*
* @param x - The value to check.
* @returns `true` if the value is a {@link PendingResult}, narrowing to `PendingResult<unknown, unknown>`.
*
* @example
* ```ts
* const x: unknown = pendingResult(ok<number, string>(42));
* const y: unknown = pendingResult(err<number, string>("failure"));
* const z: unknown = ok(42); // Not a PendingResult
*
* expect(isPendingResult(x)).toBe(true);
* expect(isPendingResult(y)).toBe(true);
* expect(isPendingResult(z)).toBe(false);
*
* if (isPendingResult(x)) {
* // Type narrowed to PendingResult<unknown, unknown>
* expect(await x).toStrictEqual(ok(42));
* }
* ```
*/
function isPendingResult(x) {
return x instanceof _PendingResult;
}
/**
* Internal implementation class for {@link Result}.
*
* Class that represents a result of an operation that might fail.
*/
class _Result {
/**
* Creates {@link Ok} invariant of {@link Result} with provided value.
*/
static ok(value) {
return new _Result({ type: "ok", value });
}
/**
* Creates {@link Err} invariant of {@link Result} with provided error.
*/
static error(error) {
if (isCheckedError(error)) {
return new _Result({ type: "error", error });
}
return new _Result({ type: "error", error: expectedError(error) });
}
/**
* Property that provides access to the value of the {@link Result}.
*
* Only {@link Ok} instances have a value, so accessing this property on {@link Err}
* will throw a {@link ResultError}.
*
* @throws
* - {@link ResultError} if `value` is accessed on {@link Err}, with
* {@link ResultErrorKind.ValueAccessedOnErr}.
*/
get value() {
if (isErr(__classPrivateFieldGet(this, __Result_state, "f"))) {
throw new ResultError("`value`: accessed on `Err`", ResultErrorKind.ValueAccessedOnErr);
}
return __classPrivateFieldGet(this, __Result_state, "f").value;
}
/**
* Property that provides access to the error of the {@link Result}.
*
* Only {@link Err} instances have an error, so accessing this property on {@link Ok}
* will throw a {@link ResultError}.
*
* @throws
* - {@link ResultError} if `error` is accessed on {@link Ok}, with
* {@link ResultErrorKind.ErrorAccessedOnOk}.
*/
get error() {
if (isOk(__classPrivateFieldGet(this, __Result_state, "f"))) {
throw new ResultError("`error`: accessed on `Ok`", ResultErrorKind.ErrorAccessedOnOk);
}
return __classPrivateFieldGet(this, __Result_state, "f").error;
}
constructor(state) {
/**
* Private field holding the raw state of the {@link Result}.
*
* Stores a {@link State} object containing either a `value` of type `T` for {@link Ok}
* instances or an `error` of type {@link CheckedError}<E> for {@link Err} instances.
* This field is managed internally to ensure type safety and encapsulation, and should
* only be accessed directly within the class’s methods.
*
* ### IMPORTANT
* **This property shall only be used within this class’s methods and constructor.**
*/
__Result_state.set(this, void 0);
__classPrivateFieldSet(this, __Result_state, state, "f");
}
and(x) {
return isOk(__classPrivateFieldGet(this, __Result_state, "f")) ? x.copy() : err(__classPrivateFieldGet(this, __Result_state, "f").error);
}
andThen(f) {
if (isErr(__classPrivateFieldGet(this, __Result_state, "f"))) {
return err(__classPrivateFieldGet(this, __Result_state, "f").error);
}
try {
return f(__classPrivateFieldGet(this, __Result_state, "f").value);
}
catch (e) {
return err(unexpectedError("`andThen`: callback `f` threw an exception", ResultErrorKind.PredicateException, e));
}
}
check() {
return isErr(__classPrivateFieldGet(this, __Result_state, "f"))
? [false, __classPrivateFieldGet(this, __Result_state, "f").error]
: [true, __classPrivateFieldGet(this, __Result_state, "f").value];
}
clone() {
if (this.isOk()) {
return ok(isPrimitive(this.value) ? this.value : this.value.clone());
}
if (this.error.isExpected()) {
return err(isPrimitive(this.error.expected)
? this.error.expected
: this.error.expected.clone());
}
return err(unexpectedError(this.error.unexpected.clone()));
}
combine(...results) {
if (isErr(__classPrivateFieldGet(this, __Result_state, "f"))) {
return err(__classPrivateFieldGet(this, __Result_state, "f").error);
}
const acc = [__classPrivateFieldGet(this, __Result_state, "f").value];
for (const result of results) {
if (result.isErr()) {
return err(result.error);
}
acc.push(result.value);
}
return ok(acc);
}
copy() {
return isOk(__classPrivateFieldGet(this, __Result_state, "f")) ? ok(__classPrivateFieldGet(this, __Result_state, "f").value) : err(__classPrivateFieldGet(this, __Result_state, "f").error);
}
err() {
if (this.isOk()) {
return none();
}
return this.error.handle(() => none(), (e) => some(e));
}
expect(msg) {
if (isOk(__classPrivateFieldGet(this, __Result_state, "f"))) {
return __classPrivateFieldGet(this, __Result_state, "f").value;
}
throw new ResultError(msg ?? "`expect`: called on `Err`", ResultErrorKind.ExpectCalledOnErr);
}
expectErr(msg) {
if (isErr(__classPrivateFieldGet(this, __Result_state, "f"))) {
return __classPrivateFieldGet(this, __Result_state, "f").error;
}
throw new ResultError(msg ?? "`expectErr`: called on `Ok`", ResultErrorKind.ExpectErrCalledOnOk);
}
flatten() {
if (this.isErr()) {
return err(this.error);
}
if (!isResult(this.value)) {
return err(unexpectedError("`flatten`: called on `Ok` with non-result value", ResultErrorKind.FlattenCalledOnFlatResult));
}
return this.value.copy();
}
inspect(f) {
if (isOk(__classPrivateFieldGet(this, __Result_state, "f"))) {
try {
const inspection = f(__classPrivateFieldGet(this, __Result_state, "f").value);
if (isPromise(inspection)) {
inspection.catch(() => void 0);
}
}
catch {
// do not care about the error
}
}
return this.copy();
}
inspectErr(f) {
if (isErr(__classPrivateFieldGet(this, __Result_state, "f"))) {
try {
const inspection = f(__classPrivateFieldGet(this, __Result_state, "f").error);
if (isPromise(inspection)) {
inspection.catch(() => void 0);
}
}
catch {
// do not care about the error
}
}
return this.copy();
}
isErr() {
return isErr(__classPrivateFieldGet(this, __Result_state, "f"));
}
isErrAnd(f) {
if (isOk(__classPrivateFieldGet(this, __Result_state, "f"))) {
return false;
}
try {
return f(__classPrivateFieldGet(this, __Result_state, "f").error);
}
catch {
return false;
}
}
isOk() {
return isOk(__classPrivateFieldGet(this, __Result_state, "f"));
}
isOkAnd(f) {
if (isErr(__classPrivateFieldGet(this, __Result_state, "f"))) {
return false;
}
try {
return f(__classPrivateFieldGet(this, __Result_state, "f").value);
}
catch {
return false;
}
}
iter() {
const state = __classPrivateFieldGet(this, __Result_state, "f");
let isConsumed = false;
return {
next() {
if (isConsumed || isErr(state)) {
// according to the specification (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols#value)
// `value` can be omitted if `done` is true
return { done: true };
}
isConsumed = true;
return { done: false, value: state.value };
},
[Symbol.iterator]() {
return this;
},
};
}
map(f) {
if (isErr(__classPrivateFieldGet(this, __Result_state, "f"))) {
return err(__classPrivateFieldGet(this, __Result_state, "f").error);
}
try {
return ok(f(__classPrivateFieldGet(this, __Result_state, "f").value));
}
catch (e) {
return err(unexpectedError("`map`: callback `f` threw an exception", ResultErrorKind.PredicateException, e));
}
}
mapAll(f) {
try {
const mapped = f(this.copy());
if (!isPromise(mapped)) {
return mapped;
}
return pendingResult(settleResult(mapped));
}
catch (e) {
return err(unexpectedError("`mapAll`: callback `f` threw an exception", ResultErrorKind.PredicateException, e));
}
}
mapErr(f) {
if (isOk(__classPrivateFieldGet(this, __Result_state, "f"))) {
return ok(__classPrivateFieldGet(this, __Result_state, "f").value);
}
return __classPrivateFieldGet(this, __Result_state, "f").error.handle((e) => err(unexpectedError(e)), (e) => {
try {
return err(f(e));
}
catch (error) {
return err(unexpectedError("`mapErr`: callback `f` threw an exception", ResultErrorKind.PredicateException, error));
}
});
}
mapOr(def, f) {
if (this.isErr()) {
return def;
}
try {
return f(this.value);
}
catch {
return def;
}
}
mapOrElse(mkDef, f) {
const makeDefault = () => {
try {
return mkDef();
}
catch (e) {
throw new ResultError("`mapOrElse`: callback `mkDef` threw an exception", ResultErrorKind.PredicateException, e);
}
};
if (this.isErr()) {
return makeDefault();
}
try {
return f(this.value);
}
catch {
return makeDefault();
}
}
match(f, g) {
try {
return this.isOk() ? f(this.value) : g(this.error);
}
catch (e) {
throw new ResultError("`match`: one of the predicates threw an exception", ResultErrorKind.PredicateException, e);
}
}
ok() {
return isOk(__classPrivateFieldGet(this, __Result_state, "f")) ? some(__classPrivateFieldGet(this, __Result_state, "f").value) : none();
}
or(x) {
if (isOk(__classPrivateFieldGet(this, __Result_state, "f"))) {
return ok(__classPrivateFieldGet(this, __Result_state, "f").value);
}
return x;
}
orElse(f) {
if (isOk(__classPrivateFieldGet(this, __Result_state, "f"))) {
return ok(__classPrivateFieldGet(this, __Result_state, "f").value);
}
try {
return f();
}
catch (e) {
return err(unexpectedError("`orElse`: callback `f` threw an exception", ResultErrorKind.PredicateException, e));
}
}
tap(f) {
try {
const r = f(this.copy());
if (isPromise(r)) {
r.catch(() => void 0);
}
}
catch {
// do not care about the error
}
return this.copy();
}
toPending() {
return pendingResult(settleResult(this.copy()));
}
toPendingCloned() {
return pendingResult(settleResult(this.clone()));
}
toString() {
return isOk(__classPrivateFieldGet(this, __Result_state, "f"))
? `Ok { ${stringify(__classPrivateFieldGet(this, __Result_state, "f").value, true)} }`
: `Err { ${stringify(__classPrivateFieldGet(this, __Result_state, "f").error, true)} }`;
}
transpose() {
if (this.isErr()) {
return some(err(this.error));
}
return isOption(this.value) ? this.value.map((x) => ok(x)) : none();
}
try() {
return isOk(__classPrivateFieldGet(this, __Result_state, "f"))
? [true, undefined, __classPrivateFieldGet(this, __Result_state, "f").value]
: [false, __classPrivateFieldGet(this, __Result_state, "f").error, undefined];
}
unwrap() {
if (isErr(__classPrivateFieldGet(this, __Result_state, "f"))) {
throw new ResultError("`unwrap`: called on `Err`", ResultErrorKind.UnwrapCalledOnErr);
}
return __classPrivateFieldGet(this, __Result_state, "f").value;
}
unwrapErr() {
if (isOk(__classPrivateFieldGet(this, __Result_state, "f"))) {
throw new ResultError("`unwrapErr`: called on `Ok`", ResultErrorKind.UnwrapErrCalledOnOk);
}
return __classPrivateFieldGet(this, __Result_state, "f").error;
}
unwrapOr(def) {
return this.isErr() ? def : this.value;
}
unwrapOrElse(mkDef) {
try {
return this.isOk() ? this.value : mkDef();
}
catch (e) {
throw new ResultError("`unwrapOrElse`: callback `mkDef` threw an exception", ResultErrorKind.PredicateException, e);
}
}
}
__Result_state = new WeakMap();
/**
* Represents a {@link Result} in a pending state that will be resolved in the future.
*
* Internally, it wraps a `Promise` that resolves to a {@link Result} on success
* or to its {@link Err} variant on failure. Methods mirror those of {@link Result},
* adapted for asynchronous resolution.
*/
class _PendingResult {
static create(result) {
return new _PendingResult(result);
}
constructor(result) {
__PendingResult_promise.set(this, void 0);
__classPrivateFieldSet(this, __PendingResult_promise, toSafePromise(result, defaultCatchMe