export class Result < T > { public isSuccess : boolean ; public isFailure : boolean ; public error : T | string ; private _value : T ; constructor ( isSuccess , error ? , value ? ) { if ( isSuccess && error ) { throw new Error ( 'InvalidOperation: A result cannot be successful and contain an error' ) ; } if ( ! isSuccess && ! error ) { throw new Error ( 'InvalidOperation: A failing result needs to contain an error message' ) ; } this . isSuccess = isSuccess ; this . isFailure = ! isSuccess ; this . error = error ; this . _value = value ; Object . freeze ( this ) ; } getValue ( ) { if ( ! this . isSuccess ) { console . log ( this . error ) ; throw new Error ( "Can't get the value of an error result. Use 'errorValue' instead." ) ; } return this . _value ; } errorValue ( ) { return this . error as T ; } static ok < U > ( value ? ) { return new Result < U > ( true , null , value ) ; } static fail < U > ( error ) { return new Result < U > ( false , error ) ; } static combine ( results ) { for ( const result of results ) { if ( result . isFailure ) return result ; } return Result . ok ( ) ; } } export type Either < L , A > = Failure < L , A > | Success < L , A > ; export class Failure < L , A > { readonly value : L ; constructor ( value ) { this . value = value ; } isFailure ( ) { return true ; } isSuccess ( ) { return false ; } } export class Success < L , A > { readonly value : A ; constructor ( value ) { this . value = value ; } isFailure ( ) { return false ; } isSuccess ( ) { return true ; } } export const failure = < L , A > ( l ) : Either < L , A > => { return new Failure ( l ) ; } ; export const success = < L , A > ( a ) : Either < L , A > => { return new Success < L , A > ( a ) ; } ;