import { Result, UnpackResult, Ok, Err, is_err, is_ok } from './result' /** `Optional.None` unwrap panic error */ export const OPTIONAL_ERROR: Error = new Error(`Unwrapped Optional.None`) /** `Optional` transpose error */ export const OPTIONAL_TRANSPOSE: Error = new TypeError(`Only Result can be transpose`) /** `Optional` type */ export const enum OptionalType { /** `Optional.Some` type */ Some, /** `Optional.None` type */ None } /** unpack a `Optional` transform to `T` */ export type UnpackOptional = O extends Optional ? T : never /** a container that have (Some) or not (None) value */ export interface Optional { /** the optional container value, readonly */ readonly value: T | never /** the optional container type, `Some` or `None`, readonly */ readonly type: OptionalType /** * predicate function, test instance was or not a Optional.Some * * @example```ts * assert.strictEqual(Some(42).is_some(), true) * assert.strictEqual(None.is_some(), false) * ``` */ is_some(): boolean /** * predicate function, test instance was or not a Optional.None * * @example```ts * assert.strictEqual(Some(42).is_none(), false) * assert.strictEqual(None.is_none(), true) * ``` */ is_none(): boolean /// ITERS /// /** * maps a `Optional` to `Optional` * * @param fn mapper function * @example * const inc = (a: number): number => a + 1 * assert.deepStrictEqual(Some(42).map(inc), Some(43)) * assert.deepStrictEqual(None.map(inc), None) */ map(fn: (v: T) => U): Optional map_async(fn: (v: T) => Promise): Promise> /** * @todo mapOrElse */ // mapOr() // mapOrElse() // forEach() /** * filter a `Optional` by a predicate function, return `None` if result is `false` * * @param fn predicate function */ filter(fn: (v: T) => boolean): Optional filter_async(fn: (v: T) => Promise): Promise> // flatMap() /** * `and` operation for Optional * * | Left | Right | Result | * |-------|-------|--------| * | SomeL | SomeR | SomeR | * | Some | None | None | * | None | None | None | * | None | None | None | * * @param opt target optional * @example```ts * assert.deepStrictEqual(Some(42).add(Some(41)), Some(41)) * assert.deepStrictEqual(Some(42).add(None), None) * assert.deepStrictEqual(None.add(Some(42)), None) * assert.deepStrictEqual(None.add(None), None) * ``` */ and(opt: Optional): Optional /** * `and` operation for lazily evaluated * * @param opt target optional */ and_then(op: (v: T) => Optional): Optional and_then_async(op: (v: T) => Promise>): Promise> /** * `or` operation for Optional * * | Left | Right | Result | * |-------|-------|--------| * | SomeL | SomeR | SomeL | * | Some | None | Some | * | None | Some | Some | * | None | None | None | * * @param opt new result */ or(opt: Optional): Optional /** * `or` operation for lazily evaluated * * @param op apply function */ or_else(op: (v: T) => Optional): Optional or_else_async(op: (v: T) => Promise>): Promise> // xor(opt: Optional): Optional /** * unwrap the container, throw when `None` */ unwrap(): T /** * unwrap the container, or get default when `None` * * @param u option value */ unwrap_or(u: U): T | U /** * `unwrapOr` operation for lazily evaluated * * @param op caller function */ unwrap_or_else(op: () => U): T | U unwrap_or_else_async(op: () => Promise): Promise /// RESULT /// /** * transform `Optional` to `Result`, mapping `Some(v)` to `Ok(v)`, and * `None` to `Err(e)` */ ok_or(e: E): Result /** * okOr for lazily evaluated * * @param e err mapper */ ok_or_else(e: () => E): Result ok_or_else_async(e: () => Promise): Promise> /** * transposes an `Optional` of a `Result` into a `Result` of an `Optional`, * 1. `None` will be mapped to `Ok(None)` * 2. `Some(Ok(v))` will be mapped to `Ok(Some(v))` * 3. `Some(Err(v))` will be mapped to `Err(v)` */ transpose>(): Result | never, U[1]> } class OptionalSome implements Optional { public readonly type = OptionalType.Some constructor(public readonly value: T) {} map(fn: (v: T) => U): Optional { // use momo's solution return new OptionalSome(fn(this.value)) /* try { return new _Some(fn(this.value)) } catch(_) { return None } */ } async map_async(fn: (v: T) => Promise): Promise> { return new OptionalSome(await fn(this.value)) } filter(fn: (v: T) => boolean): Optional { return fn(this.value) ? new OptionalSome(this.value) : None } async filter_async(fn: (v: T) => Promise): Promise> { return await fn(this.value) ? new OptionalSome(this.value) : None } is_some(): boolean { return true } is_none(): boolean { return false } and(opt: Optional): Optional { return opt } and_then(op: (v: T) => Optional): Optional { return op(this.value) } async and_then_async(op: (v: T) => Promise>): Promise> { return op(this.value) } or(_opt: Optional): Optional { return new OptionalSome(this.value) } or_else(_op: (v: T) => Optional): Optional { return new OptionalSome(this.value) } async or_else_async(_op: (v: T) => Promise>): Promise> { return new OptionalSome(this.value) } unwrap(): T { return this.value } unwrap_or(_v: U): T { return this.value } unwrap_or_else(_op: () => U): T { return this.value } async unwrap_or_else_async(_op: () => Promise): Promise { return this.value } ok_or(_e: never): Result { return Ok(this.value) } ok_or_else(_e: () => never): Result{ return Ok(this.value) } async ok_or_else_async(_e: () => Promise): Promise> { return Ok(this.value) } transpose>(): Result | never, U[1]> { if(is_err(this.value)) { return Err(this.value.unwrap_err()) } else if(is_ok(this.value)) { return Ok(Some(this.value.unwrap())) } else { throw OPTIONAL_TRANSPOSE } } } class OptionalNone implements Optional { public readonly value!: never public readonly type = OptionalType.None map(_fn: (_v: never) => U): Optional { return None } async map_async(_fn: (_v: never) => Promise): Promise> { return None } filter(_fn: (v: never) => boolean): Optional { return None } async filter_async(_fn: (v: never) => Promise): Promise> { return None } is_some(): boolean { return false } is_none(): boolean { return true } and(_opt: Optional): Optional { return None } and_then(_op: (v: never) => Optional): Optional { return None } async and_then_async(_op: (v: never) => Promise>): Promise> { return None } or(opt: Optional): Optional { return opt } or_else(op: (v: never) => Optional): Optional { return op(this.value) } async or_else_async(op: (v: never) => Promise>): Promise> { return op(this.value) } unwrap(): never { throw OPTIONAL_ERROR } unwrap_or(v: U): U { return v } unwrap_or_else(op: (e: never) => U): U { return op(this.value) } async unwrap_or_else_async(op: (e: never) => Promise): Promise { return op(this.value) } ok_or(e: E): Result { return Err(e) } ok_or_else(e: () => E): Result { return Err(e()) } async ok_or_else_async(e: () => Promise): Promise> { return Err(await e()) } transpose(): Result, never> { return Ok(None) } } /** * Optional.Some * * @param v value */ export function Some(v: T): Optional { return new OptionalSome(v) } /** Optional.None */ export const None: Optional = new OptionalNone() /// PREDICATE /// /** * test given is or not a Optional * * @param result target result */ export function is_optional(value: unknown): value is Optional { return is_some(value) || is_none(value) } /** * test value is or not Optional.Some * * @param value value */ export function is_some(value: unknown): value is Optional { return value instanceof OptionalSome } /** * test value is or not Optional.Some * * @param value value */ export function is_none(value: unknown): value is Optional { return value === None }