@onrails/result
Version:
Tagged Result / ResultAsync for railway-oriented TypeScript — pure tagged unions, neverthrow-shaped compat shim, FL-friendly
56 lines (54 loc) • 1.73 kB
TypeScript
/**
* The core railway type: a tagged union carrying either a success value
* (`Ok<T>`) or a failure value (`Err<E>`). It is a plain discriminated union
* on `_tag` — no classes — so it is immutable and tree-shake friendly. Narrow
* with {@link isOk} / {@link isErr} to read `.value` / `.error`.
*
* @typeParam T - the `Ok` value type
* @typeParam E - the `Err` error type
*
* @example
* ```ts
* function parse(raw: string): Result<number, "nan"> {
* const n = Number(raw);
* return Number.isNaN(n) ? err("nan") : ok(n);
* }
* ```
*/
type Result<T, E> = {
readonly _tag: "Ok";
readonly value: T;
} | {
readonly _tag: "Err";
readonly error: E;
};
/**
* The success branch of a {@link Result} — `{ _tag: "Ok"; value: T }`.
* The `E` parameter keeps the type assignable to `Result<T, E>`.
*/
type Ok<T, E = never> = Extract<Result<T, E>, {
_tag: "Ok";
}>;
/**
* The failure branch of a {@link Result} — `{ _tag: "Err"; error: E }`.
*/
type Err<T, E> = Extract<Result<T, E>, {
_tag: "Err";
}>;
/**
* Error subclass representing an **unexpected** defect — a thrown exception or
* promise rejection that was not part of the modelled `Err` union. Used as the
* default mapping when {@link fromAsync} is called without an `onDefect`
* handler; the original cause is preserved on `.cause`.
*
* @example
* ```ts
* const defect = new UnexpectedError("Unexpected async defect", caught);
* defect.cause; // the original thrown value
* ```
*/
declare class UnexpectedError extends Error {
readonly cause?: unknown | undefined;
constructor(message: string, cause?: unknown | undefined);
}
export { type Err as E, type Ok as O, type Result as R, UnexpectedError as U };