Skip to content

Instantly share code, notes, and snippets.

@fieldju
Created April 2, 2019 15:53
Show Gist options
  • Save fieldju/fa89ab4ac53d5bcafb25ec0968995ca3 to your computer and use it in GitHub Desktop.
Save fieldju/fa89ab4ac53d5bcafb25ec0968995ca3 to your computer and use it in GitHub Desktop.
optional-ts
export default class Optional<T> {
private readonly value: T | null | undefined;
constructor(value: T | null | undefined = null) {
this.value = value;
}
static ofNullable<T>(value: T | undefined | null): Optional<T> {
return new Optional(value);
}
static of<T>(value: T | null | undefined): Optional<T> {
if (value === undefined || value === null) {
throw new Error('value is not defined');
}
return new Optional(value);
}
static empty(): Optional<null> {
return new Optional(null);
}
/**
* If a value is present in this Optional, returns the value, otherwise throws an Error.
*
* @return the non-null value held by this Optional
* @throws Error if the value is null;
*/
get() {
if (isNull(this.value)) {
throw new Error('optional is empty');
}
return this.value;
}
/**
* Return true if there is a value present, otherwise false.
*
* @return true if there is a value present, otherwise false
*/
isPresent(): boolean {
return !isNull(this.value);
}
/**
* If a value is present, invoke the specified consumer with the value, otherwise do nothing.
*
* @param consumer function to be executed if a value is present
*/
ifPresent(consumer: (value: T) => void): void {
if (!isNull(this.value)) {
if (!isFunction(consumer)) {
throw new Error('consumer is not a function');
}
consumer(this.value as T);
}
}
/**
* If a value is present, and the value matches the given predicate, return an Optional describing the value,
* otherwise return an empty Optional.
*
* @param predicate A predicate to apply to the value, if present
* @return an Optional describing the value of this Optional if a value is present and the value matches the given
* predicate, otherwise an empty Optional
* @throws Error if the predicate is null
*/
filter(predicate: (value: T) => boolean): Optional<T | null | undefined> {
if (!isFunction(predicate)) {
throw new Error('predicate is not a function');
}
if (!isNull(this.value) && predicate(this.value as T)) {
return new Optional(this.value);
}
return new Optional();
}
/**
* If a value is present, apply the provided mapping function to it, and if the result is non-null,
* return an Optional describing the result. Otherwise return an empty Optional.
*
* @typeparam U The type of the result of the mapping function
* @param mapper a mapping function to apply to the value, if present.
* @return an Optional describing the result of applying a mapping function to the value of this Optional,
* if a value is present, otherwise an empty Optional
* @throws Error if the mapping function is null
*/
map<U>(mapper: (value: T) => U | undefined | null): Optional<U> {
var mappedValue;
if (!isFunction(mapper)) {
throw new Error('mapper is not a function');
}
if (isNull(this.value)) {
return new Optional();
}
mappedValue = mapper(this.value as T);
return isNull(mappedValue) ? new Optional() : new Optional(mappedValue);
}
/**
* If a value is present, apply the provided Optional-bearing mapping function to it, return that result,
* otherwise return an empty Optional. This method is similar to map(Function), but the provided mapper is one whose
* result is already an Optional, and if invoked, flatMap does not wrap it with an additional Optional.
*
* @typeparam U The type parameter to the Optional returned by
* @param mapper a mapping function to apply to the value, if present the mapping function
* @return the result of applying an Optional-bearing mapping function to the value of this Optional,
* if a value is present, otherwise an empty Optional
* @throws Error if the mapping function is null or returns a null result
*/
flatMap<U>(mapper: (value: T) => Optional<U> | undefined | null): Optional<U> {
var flatMappedValue;
if (!isFunction(mapper)) {
throw new Error('mapper is not a function');
}
if (isNull(this.value)) {
return new Optional();
}
flatMappedValue = mapper(this.value as T) as Optional<U>;
if (isNull(flatMappedValue) || isNull(flatMappedValue.get)) {
throw new Error('mapper does not return an Optional');
}
return flatMappedValue;
}
/**
* If a value is present, returns the value, otherwise returns other.
*
* @param other the value to be returned, if no value is present. May be null.
* @return the value, if present, otherwise other
*/
orElse(other: T): T {
return isNull(this.value) ? other : (this.value as T);
}
/**
* If a value is present, returns the value, otherwise returns the result produced by the supplying function.
*
* @param supplier the supplying function that produces a value to be returned
* @return the value, if present, otherwise the result produced by the supplying function
* @throws Error if no value is present and the supplying function is null
*/
orElseGet(supplier: () => T): T {
if (!isFunction(supplier)) {
throw new Error('supplier is not a function');
}
if (isNull(this.value)) {
return supplier();
} else {
return this.value as T;
}
}
/**
* If a value is present, returns the value, otherwise throws an exception produced by the exception supplying function.
*
* @param exceptionSupplier the supplying function that produces an exception to be thrown
* @return the value, if present
* @throws Error if no value is present and the exception supplying function is null
*/
orElseThrow(exceptionSupplier: () => Error): T {
if (isNull(this.value)) {
if (!isFunction(exceptionSupplier)) {
throw new Error('exception provider is not a function');
}
throw exceptionSupplier();
}
return this.value as T;
}
/**
* If a value is present, performs the given action with the value, otherwise performs the given empty-based action.
*
* @param action the action to be performed, if a value is present
* @param emptyAction the empty-based action to be performed, if no value is present
* @throws if a value is present and the given action is null, or no value is present and the given empty-based action is null.
*/
ifPresentOrElse(action: (value: T) => void, emptyAction: () => void) {
if (!isNull(this.value)) {
if (!isFunction(action)) {
throw new Error('action is not a function');
}
action(this.value as T);
} else {
if (!isFunction(emptyAction)) {
throw new Error('emptyAction is not a function');
}
emptyAction();
}
}
/**
* If a value is present, returns an Optional describing the value, otherwise returns an Optional produced by the supplying function.
*
* @param optionalSupplier the supplying function that produces an Optional to be returned
* @return returns an Optional describing the value of this Optional, if a value is present,
* otherwise an Optional produced by the supplying function.
* @throws Error if the supplying function is null or produces a null result
*/
or(optionalSupplier: () => Optional<T>): Optional<T> {
if (isNull(this.value)) {
if (!isFunction(optionalSupplier)) {
throw new Error('optionalSupplier is not a function');
}
return optionalSupplier();
}
return this;
}
}
const isNull = (value: any) => {
return value === undefined || value === null;
};
const isFunction = (value: any) => {
return typeof value === 'function';
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment