type empty = null | undefined; export abstract class Option { abstract get(): T abstract expect(err: Error): T; abstract orElse(fn: () =>T): T abstract orDefault(t: T): T abstract map(fn: (a: T) => Y): Option abstract filter(fn: (a: T) => boolean): Option; abstract or(another: Option): Option abstract ifPresent(fn: (t: T) => void): void abstract isPresent(): boolean; isAbsent(): boolean { return !this.isPresent() } static none () { return new None() } static some(value: Y) { return new Some(value) } static fromNullable(valueOrNull: Y | empty) { if (valueOrNull === null || valueOrNull === undefined) { return this.none() } else { return this.some(valueOrNull) } } } export class Some extends Option { value: T constructor(t: T) { super() this.value = t } get(): T { return this.value; } expect (_err: Error): T { return this.value } orElse(_fn: () => T): T { return this.value; } map(fn: (a: T) => Y): Option { return new Some(fn(this.value)); } filter (fn: (a: T) => boolean): Option { if (fn(this.value)) { return this } else { return Option.none() } } orDefault(_t: T): T { return this.value; } or(another: Option): Option { return this; } ifPresent(fn: (t: T) => void): void { fn(this.value) } isPresent (): boolean { return true; } } export class None extends Option { get(): T { throw new Error('Not present'); } expect (err: Error): T { throw err } orElse(fn: () => T): T { return fn(); } map(fn: (a: T) => Y): Option { return new None(); } filter (fn: (a: T) => boolean): Option { return this; } orDefault(t: T): T { return t; } or(another: Option): Option { return another; } ifPresent(fn: (t: T) => void): void { // no-op } isPresent (): boolean { return false; } }