Created
April 2, 2019 15:53
-
-
Save fieldju/fa89ab4ac53d5bcafb25ec0968995ca3 to your computer and use it in GitHub Desktop.
optional-ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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