interface BaseResult { map(fn: (data: T) => N): Result; mapErr(fn: (data: E) => N): Result; isOk(): this is ResultOk; isErr(): this is ResultErr; unwrap(): T; } export class UnwrapError extends Error { constructor(public readonly data: E) { super('Operation has resulted in an error.'); } } export type Result = ResultOk | ResultErr; export type ResultPromise = Promise>; export class R { public static map( transform: (data: T) => Result | Promise>, ): (result: Result) => Promise>; public static map(transform: (data: T) => N | Promise): (result: Result) => Promise>; public static map( transform: (data: T) => N | Promise, ): (result: Result) => Promise> { return async (result) => (result.isErr() ? result : wrapOk(await transform(result.data))); } public static mapErr( transform: (data: E) => Result | Promise>, ): (result: Result) => Promise>; public static mapErr( transform: (data: E) => N | Promise, ): (result: Result) => Promise>; public static mapErr( transform: (data: E) => N | Promise, ): (result: Result) => Promise> { return async (result) => (result.isOk() ? result : wrapErr(await transform(result.data))); } public static flatMap(results: Result[]): Result { const data: T[] = []; for (const result of results) { if (result.isErr()) { return result; } data.push(result.data); } return Ok(data); } } export class ResultOk implements BaseResult { constructor(public readonly data: T) {} public map(transform: (data: T) => N): Result { return new ResultOk(transform(this.data)); } public mapErr(): Result { return this; } public isOk(): this is ResultOk { return true; } public isErr(): this is ResultErr { return false; } public unwrap(): T { return this.data; } } export class ResultErr implements BaseResult { constructor(public readonly data: E) {} public map(): Result { return this; } public mapErr(fn: (data: E) => N): Result { return new ResultErr(fn(this.data)); } public isOk(): this is ResultOk { return false; } public isErr(): this is ResultErr { return true; } public unwrap(): never { throw new UnwrapError(this.data); } } export function Ok(data: T): Result { return new ResultOk(data); } export function Err(data: E): Result { return new ResultErr(data); } export function wrapOk(data: T | Result): Result { if (data instanceof ResultOk || data instanceof ResultErr) { return data; } return Ok(data); } export function wrapErr(data: E | Result): Result { if (data instanceof ResultOk || data instanceof ResultErr) { return data; } return Err(data); }