This module contains small class which is inspired by Java Optional and was created to provide earier ways to work with possibly nulled/undefined values.
This library is heavily inspired by Optional from standard java library, but doesn't follow it precisely. If you need exact port, there is typescript-optional implementation.
Please note that this project follows semantic versioning approach and may change API in between pre-1.0 minor releases.
npm i -S @ama-team/optional
or
yarn add @ama-team/optional
Optional
is just a class that wraps possibly-null/undefined value and
apply operations to it if it is present. For example, you have a
response from API that optionally has a metadata field with a flag
deeply buried inside the response:
user:
id: 1
...
metadata:
processors:
fraud:
fraudulent: true
Instead of checking presence of the fields, you can take a shortcut:
import {Optional} from '@ama-team/optional'
const fraudulent = Optional
.of(user)
.map(user => user.processors)
.map(processors => processors.fraud)
.map(metadata => metadata.fraudulent)
.orElse(false);
Bingo, now you either have true
or false
without too much hassle.
I'll show how to work with pipelines like the one above later.
You have three ways to create an optional:
Optional.empty();
Optional.of(value); // throws error if value is null / undefined
Optional.ofNullable(value);
Optional has two properties and several methods to interact with current state:
optional.present; // boolean
optional.empty; // also boolean
// following methods returns Optional itself, allowing to chain methods
optional.ifPresent(identity => {});
optional.ifEmpty(() => {});
optional.peek(identity => {}); // will substitute missing identity with null
optional.on(identity => {}, () => {}); // will trigger one of those depending on identity presence
You can also suggest a value for optional, which will be used as identity if optional is empty:
optional.rescue(value);
optional.rescueWith(() => value);
There are also several ways to retrieve identity or substitute it with value:
optional.get(); // will throw TypeError on missing entity
optional.orElse(value); // return identity or value
optional.orElseGet(() => value); // return identity or producer result
optional.orElseThrow(() => new Error()); // throw error using producer
And, finally, there are transformation methods:
// all those methods aren't mutating optional itself - transformations
// will be applied eagerly to construct new optional
optional.map(identity => transform(identity));
optional.flatMap(identity => Optional.of(identity)); // unwraps returned optional
optional.filter(identity => true); // substitutes identity with null if filter doesn't return true
Optionals help with checking for null/undefined, but generally the example above bloats code as well, and usually should be placed otherwise. To do so, pipelines were introduced: encapsulated set of lazy operations over optional:
import {Pipeline} from '@ama-team/optional';
const extractor = Pipeline.create()
.map(user => user.processors)
.map(processors => processors.fraud)
.map(metadata => metadata.fraudulent);
// ...
const fraudulent = extractor.apply(user).orElse(false);
Pipelines are very simple, and currently they provide two methods to process value:
.apply(value)
, which will return an Optional and will let you to use.orElse()
or similar methods.transform(value)
, which will in fact just a shortcut for.apply(value).orElse(null)
.
Also pipelines can be concatenated - you can use .append(pipeline)
method for that, which will return you a new pipeline.
Feel free to send PR to dev
branch.
MIT License / AMA Team / 2018