import AssertionError from "../error/AssertionError"; export default class Exceptional { private constructor(private value: T, private exception: Error) { if ((value == null) == (exception == null)) { throw new AssertionError("Value and exception cannot both be initialized or both be null."); } } private static getDefaultException(): Error { return new Error(); } public static empty(exception: Error = Exceptional.getDefaultException()): Exceptional { return Exceptional.ofNullable(null, exception); } public filter(predicate: (T) => boolean, value: Error | ((T) => Error) = t => Exceptional.getDefaultException()): Exceptional { return this.isPresent() ? (predicate(this.value) ? this : Exceptional.ofNullable(null, value instanceof Error ? value as Error : value(this.value) ) ) : this; } public flatMap(mapper: (T) => Exceptional): Exceptional { return this.isPresent() ? mapper(this.get()) : Exceptional.empty(); } public get(): T { if (!this.isPresent()) { throw this.exception; } return this.value; } public ifPresent(consumer: (T) => void): void { if (this.isPresent()) { consumer(this.value); } } public isPresent(): boolean { return this.value != null; } public map(mapper: (T) => U, value: Error | ((T) => Error) = t => Exceptional.getDefaultException()): Exceptional { if (!this.isPresent()) { return Exceptional.empty(value instanceof Error ? value as Error : value(this.value) ); } const u: U = mapper(this.value); return Exceptional.ofNullable(u, u == null ? value instanceof Error ? value as Error : value(this.value) : null ); } public static of(value: T, exception: Error = Exceptional.getDefaultException()): Exceptional { if (value == null) { throw exception; } return new Exceptional(value, null); } public static ofNullable(value: T, exception: Error = Exceptional.getDefaultException()): Exceptional { return new Exceptional(value, value == null ? exception : null); } public orElse(other: T): T { return this.isPresent() ? this.value : other; } public orElseGet(other: () => T): T { return this.isPresent() ? this.value : other(); } public orElseThrow(exceptionSupplier: () => X): T { if (!this.isPresent()) { throw exceptionSupplier(); } return this.value; } public getException(): Error { return this.exception; } public withException(exception: Error): Exceptional { return this.isPresent() ? this : Exceptional.empty(exception); } }