import { Either , either , right , isRight } from "fp-ts/lib/Either" ; import * as t from "io-ts" ; import { Nil , Predicate } from "./types" ; import { isDefined , isNil } from "./utility/primitive" ; param type param preferNull export function option < T extends t . Mixed > ( type , preferNull = true ) { return new t . Type < Option < t . TypeOf < T > > , t . OutputOf < T > | Nil , unknown > ( `Option<${ type . name }>` , ( u ) : u is Option < t . TypeOf < T > > => { if ( isNil ( u ) ) return true ; return type . is ( u ) ; } , ( u , c ) => { if ( isNil ( u ) ) return right ( None ) ; const decodeResult = type . validate ( u , c ) ; return either . chain ( decodeResult , ( a ) : Either < t . Errors , Option < t . TypeOf < T > > > => right ( Some ( a ) ) ) ; } , ( a ) : t . OutputOf < T > | Nil => { const outputOption = a . map ( type . encode ) ; return preferNull ? outputOption . orNull ( ) : outputOption . orUndefined ( ) ; } ) ; } export type Unwrap < T > = T extends Option < infer K > ? K : T ; export interface OptionLike < A > { /**
     * Whether the option is `None`
     * **Note: does not act as a type guard**
     */ _ : boolean ; /**
     * Whether the option is `None`
     */ isEmpty ( ) : this is NoneType ; /**
     * Whether the option is `Some(...)`
     */ isDefined ( ) : this is SomeType < A > ; /**
     * Gets the inner value of the option if it is defined, else `null`
     */ orNull ( ) : A | null ; /**
     * Gets the inner value of the option if it is defined, else `undefined`
     */ orUndefined ( ) : A | undefined ; /**
     * Gets the value of the Option, using the default value given if the option itself is
     * None
     */ getOrElse ( _ ) : A ; param func map < B > ( func ) : OptionLike < B > ; param func filter ( func ) : OptionLike < A > ; param func flatMap < B > ( func ) : OptionLike < B > ; param func flatMapNil < B > ( func ) : OptionLike < B > ; param m match < B > ( m ) : B ; param func returns forEach ( func ) : OptionLike < A > ; param other param compare equals ( other , compare ? ) : boolean ; param other returns or ( other ) : OptionLike < A > ; param producer returns orElse ( producer ) : OptionLike < A > ; param other returns zip < B > ( other ) : OptionLike < [ A , B ] > ; } interface ValuedOption < A > { readonly get : A ; } /**
 * Represents a value that either may exist or may not. To determine whether the
 * value exists, use `option.isEmpty`.
 */ export abstract class Option < A > implements OptionLike < A > { abstract isEmpty ( ) ; abstract isDefined ( ) ; abstract _ : boolean ; abstract getOrElse < B = A > ( _ ) ; abstract map < B > ( _ ) ; abstract filter ( _ ) ; abstract flatMap < B > ( _ ) ; abstract flatMapNil < B > ( _ ) ; abstract match < B > ( m ) ; abstract forEach ( _ ) ; abstract or ( _ ) ; abstract zip < B > ( _ ) ; abstract orElse ( _ ) ; abstract equals ( other , compare ? ) ; orNull ( ) { return null ; } orUndefined ( ) { return undefined ; } param value static from < A > ( value ) { if ( isDefined ( value ) ) return Some ( value ) ; return None ; } param cond static if ( cond ) { if ( cond ) return Some ( { } ) ; return None ; } /**
     * Drops an either into an option
     */ static drop < A > ( e ) { if ( isRight ( e ) ) return Some ( e . right ) ; return None ; } param op2 param op1 static merge < A , B > ( op1 , op2 ) { if ( op1 . isDefined ( ) && op2 . isDefined ( ) ) return Some ( [ op1 . get , op2 . get ] ) ; return None ; } param source param trim static fromString ( source , trim = false ) { if ( isNil ( source ) || ( trim ? source . trim ( ) : source ) === "" ) return None ; return Option . from ( source ) ; } } export interface Matcher < A , B > { Some : ( _ : A ) => B ; None : ( ) => B ; } export class SomeType < A > extends Option < A > implements ValuedOption < A > { _ = false ; v : A ; constructor ( value ) { super ( ) ; this . v = value ; } isDefined ( ) { return true ; } isEmpty ( ) { return false ; } get get ( ) : A { return this . v ; } orNull ( ) { return this . v ; } orUndefined ( ) { return this . v ; } getOrElse < B > ( _ ) { return this . v ; } map < B > ( _ ) { return Some ( _ ( this . v ) ) ; } filter ( _ ) { const result = _ ( this . v ) ; return result ? this : None ; } flatMap < B > ( _ ) { return _ ( this . v ) ; } flatMapNil < B > ( _ ) { const result = _ ( this . v ) ; if ( isNil ( result ) ) return None ; return Some ( result ) ; } match < B > ( m ) { return m . Some ( this . v ) ; } forEach ( _ ) { _ ( this . v ) ; return this ; } equals ( other , compare ? ) { if ( ! other . isDefined ( ) ) return false ; if ( isDefined ( compare ) ) return compare ( this . v , other . v ) ; return this . v === other . v ; } or ( _ ) { return this ; } orElse ( _ ) { return this ; } zip < B > ( other ) { return other . map ( ( value ) => [ this . get , value ] ) ; } } export class NoneType extends Option < never > { private static inst : NoneType | undefined = undefined ; private constructor ( ) { super ( ) ; } public static get instance ( ) : NoneType { if ( isNil ( NoneType . inst ) ) { NoneType . inst = new NoneType ( ) ; } return NoneType . inst ; } _ = true ; isDefined ( ) { return false ; } isEmpty ( ) { return true ; } getOrElse ( _ ) { return _ ; } map < B > ( _ ) { return None ; } filter ( _ ) { return None ; } flatMap < B > ( _ ) { return None ; } flatMapNil < B > ( _ ) { return None ; } match < B > ( m ) { return m . None ( ) ; } forEach ( _ ) { return ( this as unknown ) as Option < never > ; } equals ( other ) { return other . _ ; } or ( other ) { return other ; } orElse ( producer ) { return producer ( ) ; } zip < B > ( _ ) { return None ; } } param value export function Some < A > ( value ) { return new SomeType ( value ) ; } /**
 * Represents the singleton None object that represents no value in an Option<T>
 */ export const None = NoneType . instance ;