import { toString } from './utils' ; import { Result , Ok , Err } from './result' ; interface BaseOption < T > extends Iterable < T extends Iterable < infer U > ? U : never > { /** `true` when the Option is Some */ readonly some : boolean ; /** `true` when the Option is None */ readonly none : boolean ; param msg expect ( msg ) : T ; /**
     * Returns the contained `Some` value.
     * Because this function may throw, its use is generally discouraged.
     * Instead, prefer to handle the `None` case explicitly.
     *
     * Throws if the value is `None`.
     */ unwrap ( ) : T ; /**
     * Returns the contained `Some` value or a provided default.
     *
     *  (This is the `unwrap_or` in rust)
     */ unwrapOr < T2 > ( val ) : T | T2 ; /**
     * Calls `mapper` if the Option is `Some`, otherwise returns `None`.
     * This function can be used for control flow based on `Option` values.
     */ andThen < T2 > ( mapper ) : Option < T2 > ; /**
     * Maps an `Option<T>` to `Option<U>` by applying a function to a contained `Some` value,
     * leaving a `None` value untouched.
     *
     * This function can be used to compose the Options of two functions.
     */ map < U > ( mapper ) : Option < U > ; /**
     * Maps an `Option<T>` to a `Result<T, E>`.
     */ toResult < E > ( error ) : Result < T , E > ; } /**
 * Contains the None value
 */ class NoneImpl implements BaseOption < never > { readonly some = false ; readonly none = true ; [ Symbol . iterator ] ( ) { return { next ( ) { return { done : true , value : undefined ! } ; } , } ; } unwrapOr < T2 > ( val ) { return val ; } expect ( msg ) { throw new Error ( `${ msg }` ) ; } unwrap ( ) { throw new Error ( `Tried to unwrap None` ) ; } map < T2 > ( _mapper ) { return this ; } andThen < T2 > ( op ) { return this ; } toResult < E > ( error ) { return Err ( error ) ; } toString ( ) { return 'None' ; } } export const None = new NoneImpl ( ) ; export type None = NoneImpl ; Object . freeze ( None ) ; /**
 * Contains the success value
 */ class SomeImpl < T > implements BaseOption < T > { static readonly EMPTY = new SomeImpl < void > ( undefined ) ; readonly some ! : true ; readonly none ! : false ; readonly val ! : T ; /**
     * Helper function if you know you have an Some<T> and T is iterable
     */ [ Symbol . iterator ] ( ) { const obj = Object ( this . val ) as Iterable < any > ; return Symbol . iterator in obj ? obj [ Symbol . iterator ] ( ) : { next ( ) { return { done : true , value : undefined ! } ; } , } ; } constructor ( val ) { if ( ! ( this instanceof SomeImpl ) ) { return new SomeImpl ( val ) ; } this . some = true ; this . none = false ; this . val = val ; } unwrapOr ( _val ) { return this . val ; } expect ( _msg ) { return this . val ; } unwrap ( ) { return this . val ; } map < T2 > ( mapper ) { return Some ( mapper ( this . val ) ) ; } andThen < T2 > ( mapper ) { return mapper ( this . val ) ; } toResult < E > ( error ) { return Ok ( this . val ) ; } /**
     * Returns the contained `Some` value, but never throws.
     * Unlike `unwrap()`, this method doesn't throw and is only callable on an Some<T>
     *
     * Therefore, it can be used instead of `unwrap()` as a maintainability safeguard
     * that will fail to compile if the type of the Option is later changed to a None that can actually occur.
     *
     * (this is the `into_Some()` in rust)
     */ safeUnwrap ( ) { return this . val ; } toString ( ) { return `Some(${ toString ( this . val ) })` ; } } export const Some = SomeImpl as typeof SomeImpl & ( < T > ( val : T ) => SomeImpl < T > ) ; export type Some < T > = SomeImpl < T > ; export type Option < T > = Some < T > | None ; export type OptionSomeType < T extends Option < any > > = T extends Some < infer U > ? U : never ; export type OptionSomeTypes < T extends Option < any > [ ] > = { [ key in keyof T ] : T [ key ] extends Option < any > ? OptionSomeType < T [ key ] > : never ; } ; export namespace Option { /**
     * Parse a set of `Option`s, returning an array of all `Some` values.
     * Short circuits with the first `None` found, if any
     */ export function all < T extends Option < any > [ ] > ( ... options ) { const someOption = [ ] ; for ( let option of options ) { if ( option . some ) { someOption . push ( option . val ) ; } else { return option as None ; } } return Some ( someOption as OptionSomeTypes < T > ) ; } /**
     * Parse a set of `Option`s, short-circuits when an input value is `Some`.
     * If no `Some` is found, returns `None`.
     */ export function any < T extends Option < any > [ ] > ( ... options ) { for ( const option of options ) { if ( option . some ) { return option as Some < OptionSomeTypes < T > [ number ] > ; } else { return option as None ; } } return None ; } export function isOption < T = any > ( value ) { return value instanceof Some || value === None ; } }