// ── Core Types ─────────────────────────────────────────── export type Ok = { readonly ok: true readonly value: ValueType readonly error?: undefined } export type Err = { readonly ok: false readonly error: ErrorType readonly value?: undefined } /** * Union type representing either a successful (Ok) or failed (Err) computation result. * * @typeparam ValueType - The type of the value in a successful result * @typeparam ErrorType - The type of the error in a failed result */ export type Result = | Ok | Err // ── Constructors ───────────────────────────────────────── /** * Construct a successful Result containing a value. * * @example * const success = ok(42) * console.log(success.value) // 42 */ export function ok(value: T): Ok /** * Construct a successful Result with undefined value. * * @example * const success = ok() * console.log(success.value) // undefined */ export function ok(): Ok export function ok(value?: T): Ok { return { ok: true, value: value as T } } /** * Construct an error Result with a type and payload. * * @example * err('Timeout', { ms: 1000 }) // Err<{ type: 'Timeout', ms: 1000 }> */ export function err>( type: K, payload: P, ): Err<{ type: K } & P> /** * Construct an error Result with only a type. * * @example * err('Timeout') // Err<{ type: 'Timeout' }> */ export function err(type: K): Err /** * Construct an error Result from an arbitrary error value (object, string, etc). * Type safety and pattern matching only work if the value is an object with a `type` property. * * @example * err({ message: 'Something failed' }) * err(new Error('fail')) */ export function err(payload: K): Err export function err(typeOrPayload: any, payload?: any): Err { if (payload !== undefined) { return { ok: false, error: { type: typeOrPayload, ...payload } } } return { ok: false, error: typeOrPayload } } /** * Helper to embed a cause error payload when constructing a new Err. * * @example * const low = err('Low') * const high = err('High', cause(low)) */ export function cause(e: T): { cause: T["error"] } { return { cause: e.error } } // ── Utility Functions (functional style) ──────────────── export const map = ( r: Result, fn: (v: ValueType) => NewValue, ): Result => (r.ok ? ok(fn(r.value)) : r) export const mapErr = ( r: Result, fn: (e: ErrorType) => NewError, ): Result => (r.ok ? r : err(fn(r.error))) export const flatMap = ( r: Result, fn: (v: ValueType) => Result, ): Result => (r.ok ? fn(r.value) : r) export function unwrap( r: Result, ): ValueType export function unwrap( r: PromiseLike>, ): Promise export function unwrap( r: Result | PromiseLike>, ): ValueType | Promise { if (isPromiseLike>(r)) { return Promise.resolve(r).then(value => unwrap(value)) } if (r.ok) return r.value throw r.error } export const orElse = ( r: Result, fallback: ValueType, ): ValueType => (r.ok ? r.value : fallback) // ── Universal factory / wrapper / rehydrator ──────────── const isPromiseLike = (v: unknown): v is PromiseLike => typeof v === "object" && v !== null && "then" in v export function result(work: () => ValueType): Result export function result( work: PromiseLike, ): Promise> export function result( work: (() => ValueType) | PromiseLike, ): Result | Promise> { if (isPromiseLike(work)) { return Promise.resolve(work) .then(ok) .catch((e: unknown) => err(e as ErrorType)) } try { return ok((work as () => ValueType)()) } catch (e) { return err(e as ErrorType) } } /** * Pattern match on a Result object (Ok/Err) or a discriminant string. * * Overloads: * - match(result, { ok, err }) * - match(type, { A: fn, B: fn }) */ export function match( r: Result, arms: { ok: (v: ValueType) => OnOk; err: (e: ErrorType) => OnErr }, ): OnOk | OnErr export function match< T extends string & (string extends T ? never : unknown), Cases extends { [K in T]: () => unknown }, >( value: T, cases: Cases & Record, never>, ): ReturnType export function match(a: any, b: any): any { if (typeof a === "object" && a && "ok" in a) { return a.ok ? b.ok(a.value) : b.err(a.error) } return b[a]() }