UNPKG

ts-std-lib

Version:
272 lines (229 loc) 9.12 kB
import { InspectOptions } from 'util'; import { IEquatable, equals } from './Equality'; import { IFilterable } from './IFilterable'; import { IMappable } from './IMappable'; import { IChainable } from './IChainable'; import { Nullable } from './Nullable'; import { Undefinable } from './Undefinable'; import { IEqualityComparer, DefaultEqualityComparer } from './Equality'; import { IInspectable, inspect } from './IInspectable'; import { IInspector } from './IInspector'; import { DefaultInspector } from './DefaultInspector'; import { Type } from './Type'; /** * Error for the optional values not set */ export class OptionalValueNotSetError extends Error { public constructor() { super('Value is null'); } } /** * An object to wrap a nullable in an interface to avoid null exeptions. * * @see https://docs.oracle.com/javase/8/docs/api/java/util/Optional.html * @see http://www.baeldung.com/java-optional */ export class Optional<T> implements IEquatable<Optional<T>>, IFilterable<T>, IMappable<T>, IChainable<T>, IInspectable { private readonly _value: Nullable<T>; private readonly _equalityComparer: Nullable<IEqualityComparer<T>>; private readonly _inspector: Nullable<IInspector<T>>; // tslint:disable:unified-signatures /** * Creates an empty instance of Optional. No value is present for this Optional. * @param equalityComparer An optional equality comparer for the value. * @param inspector An optional inspector for the value. */ public constructor(value?: undefined, equalityComparer?: IEqualityComparer<T>, inspector?: IInspector<T>); /** * Creates an instance of Optional describing the specified value, if defined, otherwise returns an empty Optional. * @param value A possibly null value. * @param equalityComparer An optional equality comparer for the value. * @param inspector An optional inspector for the value. */ public constructor(value: Undefinable<T>, equalityComparer?: IEqualityComparer<T>, inspector?: IInspector<T>); /** * Creates an instance of Optional with the specified present non-null and defined value. * @param value A non-nullable value. * @param equalityComparer An optional equality comparer for the value. * @param inspector An optional inspector for the value. */ public constructor(value: T, equalityComparer?: IEqualityComparer<T>, inspector?: IInspector<T>); /** * Creates an instance of Optional describing the specified value, if non-null, otherwise returns an empty Optional. * @param value A possibly null value. * @param equalityComparer An optional equality comparer for the value. * @param inspector An optional inspector for the value. */ public constructor(value: Nullable<T>, equalityComparer?: IEqualityComparer<T>, inspector?: IInspector<T>); public constructor(value: Undefinable<Nullable<T>>, equalityComparer?: IEqualityComparer<T>, inspector?: IInspector<T>); public constructor(value?: Nullable<T>, equalityComparer?: IEqualityComparer<T>, inspector?: IInspector<T>) { this._value = value === undefined ? null : value; this._equalityComparer = equalityComparer || null; this._inspector = inspector || null; } // tslint:enable:unified-signatures public [equals](other: Optional<T>): boolean { if (!this.isValuePresent(this._value)) { return !other.isPresent(); } if (!other.isPresent()) { return false; } const valueEqualityComparer = this._equalityComparer || other._equalityComparer || new DefaultEqualityComparer(); return valueEqualityComparer.equals(this._value, other.get()); } public [inspect](options?: InspectOptions): string { if (!this.isValuePresent(this._value)) { return `<${Optional.name}> [empty]`; } const inspector = this._inspector || new DefaultInspector(); return `<${Optional.name}> ${inspector.inspect(this._value, options)}`; } /** * Check if the value is null. * * @returns True if there is a value present, otherwise false. */ public isPresent(): boolean { return this.isValuePresent(this._value); } /** * If a value is present, invoke the specified callback with the value, otherwise do nothing. * * @param callback The callback to invoke. */ public ifPresent(callback: (value: T) => void): void { if (this.isValuePresent(this._value)) { callback(this._value); } } /** * If a value is present, performs the given callback with the value, otherwise performs the given empty-based callback. * * @param callback The callback to be performed, if a value is present. * @param emptyCallback The callback to be performed, if no value is present. */ public ifPresentOrElse(callback: (value: T) => void, emptyCallback: () => void): void { if (this.isValuePresent(this._value)) { return callback(this._value); } return emptyCallback(); } /** * If a value is present, returns an Optional describing the value, otherwise returns an Optional produced by the supplying function. * * @param supplier The supplying function that produces an Optional to be returned. * @returns An Optional describing the value of this Optional, if a value is present, otherwise an Optional produced by the supplying function. */ public or(supplier: () => Optional<T>): Optional<T> { if (this.isValuePresent(this._value)) { return this; } return supplier(); } /** * Return the value if not null or a default. * * @param defaultValue The default value to return. * @returns The value if present, otherwise return the default. * @description The methods orElse and orElseGet are generally preferable to this method, as they return a substitute value if the value is absent, instead of throwing an exception. */ public orElse(supplier: T): T { if (this.isValuePresent(this._value)) { return this._value; } return supplier; } /** * Return the value if not null or a default via a callback, to allow for lazy loading. * * @param callback The callback to invoke to get the default value to return. * @returns The value if present, otherwise return the default. */ public orElseGet(callback: () => T): T { if (this.isValuePresent(this._value)) { return this._value; } return callback(); } /** * Return the contained value, if present, otherwise throw an exception to be created by the provided supplier. * * @param exceptionSupplier The callback to invoke to get the error to throw. * @returns The value if present. */ public orElseThrow(exceptionSupplier: () => Error): T { if (this.isValuePresent(this._value)) { return this._value; } throw exceptionSupplier(); } /** * Return the contained value, if present, otherwise return 'undefined'. * * @returns {Undefinable<T>} */ public orUndefined(): Undefinable<T> { if (this.isValuePresent(this._value)) { return this._value; } return undefined; } /** * Return the contained value, if present, otherwise return 'null'. * * @returns {Undefinable<T>} */ public orNull(): Nullable<T> { if (this.isValuePresent(this._value)) { return this._value; } return null; } /** * If a value is present in this Optional, returns the value, otherwise throws NoSuchElementException. * * @returns {T} The value if present. */ public get(): T { if (this.isValuePresent(this._value)) { return this._value; } throw new OptionalValueNotSetError(); } public filter<S extends T>(predicate: (value: T) => value is S): Optional<S>; public filter(predicate: (value: T) => boolean): Optional<T>; public filter(predicate: (value: T) => value is T): Optional<T> { if (this.isValuePresent(this._value)) { if (predicate(this._value)) { return this; } return new Optional(undefined, this._equalityComparer || undefined); } return new Optional(undefined, this._equalityComparer || undefined); } public map<U>(callbackfn: (value: T) => U, equalityComparer?: IEqualityComparer<U>): Optional<U> { if (this.isValuePresent(this._value)) { const value = callbackfn(this._value); return new Optional(value, equalityComparer); } return new Optional(undefined, equalityComparer); } public chain<U>(callbackfn: (value: T) => Optional<U>, equalityComparer?: IEqualityComparer<U>): Optional<U>; // tslint:disable-next-line:unified-signatures public chain<U>(callbackfn: (value: T) => U, equalityComparer?: IEqualityComparer<U>): Optional<U>; public chain<U>(callbackfn: (value: T) => Optional<U> | U, equalityComparer?: IEqualityComparer<U>): Optional<U> { if (this.isValuePresent(this._value)) { const value = callbackfn(this._value); if (Type.isInstanceOf(Optional, value)) { return value; } return new Optional(value, equalityComparer); } return new Optional(undefined, equalityComparer); } private isValuePresent(value: Nullable<T>): value is T { return value !== null; } }