import { isObject } from './valueIsObject'; export type Result = Success | Failure; type Success = { chain(fn: (value: T) => Result): Result; map(fn: (value: T) => U): Result; mapError(fn: (error: E) => E2): Result; match(handlers: { ok: (value: T) => U; err: (error: E) => U }): U; readonly type: 'ok'; unwrap(): T; unwrapOr(defaultValue: T): T; readonly value: T; }; export type Failure = { chain(fn: (value: T) => Result): Result; readonly error: E; map(fn: (value: T) => U): Result; mapError(fn: (error: E) => E2): Result; match(handlers: { ok: (value: T) => U; err: (error: E) => U }): U; readonly type: 'err'; unwrap(): T; unwrapOr(defaultValue: T): T; }; function ok(value: T): Success { return { chain(fn: (inner: T) => Result) { return fn(value) as Result; }, map(fn: (inner: T) => U) { return ok(fn(value)); }, mapError(_fn: (error: E) => E2) { return this as unknown as Success; }, match(handlers: { ok: (val: T) => U; err: (err: E) => U }) { return handlers.ok(value); }, type: 'ok', unwrap() { return value; }, unwrapOr(_defaultValue: T) { return value; }, value, }; } function err(error: E): Failure { return { chain(_fn: (inner: T) => Result) { return this as unknown as Failure; }, error, map(_fn: (inner: T) => U) { return this as unknown as Failure; }, mapError(fn: (inner: E) => E2) { return err(fn(error)); }, match(handlers: { ok: (val: T) => U; err: (err: E) => U }) { return handlers.err(error); }, type: 'err', unwrap() { throw error instanceof Error ? error : new Error(String(error)); }, unwrapOr(defaultValue: T) { return defaultValue; }, }; } export const makeResult = { Ok: ok, Err: err } as const; const hasResultType = (candidate: { type?: unknown }): boolean => candidate.type === 'ok' || candidate.type === 'err'; const hasResultShape = (candidate: Record): boolean => 'value' in candidate || 'error' in candidate; export function isResult(value: unknown): value is Result { if (!isObject(value)) { return false; } const candidate = value as { type?: unknown } & Record; if (!hasResultShape(candidate)) { return false; } return hasResultType(candidate); } export function isSuccess(value: Result): value is Success { return value.type === 'ok'; } export function isFailure(value: Result): value is Failure { return value.type === 'err'; } /** * Unwraps a Result. * If the Result is Success, it returns the value. * If the Result is Failure, it throws the error. */ export function unwrap(result: Result): T { if (isFailure(result)) { // Ensure we always throw an Error object for better stack traces throw result.error instanceof Error ? result.error : new Error(String(result.error)); } return result.value; }