I'm looking for a client-side JavaScript library that would let me write code similar to what I can do in other languages using some flavour of Option type, for example java.lang.Optional.
My target is to avoid null
/undefined
checks in client code and make API more explicit.
Here is the API I want to be able to write:
var dictionary = {
key1: 'value1',
key2: 'value2'
}
function getValue(key){
var value = dictionary[key];
if(value !== null && value !== undefined)
return Optional.of(value);
else
return Optional.empty();
}
And here is the client code:
var value = getValue('key3');
if(value.isPresent())
console.log('got a value: ' + value.get().toUpperCase());
else
console.log('no value found!');
Or sometimes:
var value = getValue('unknown key').orElse('default value');
If I call get()
on Optional.empty()
value, it should throw some kind of Error
.
If I call Optional.of()
on null
or undefined
, it should throw as well.
Optional of() method in Java with examples Parameters: This method accepts value as parameter of type T to create an Optional instance with this value. Return value: This method returns an instance of this Optional class with the specified value of the specified type.
The orElse() method of java. util. Optional class in Java is used to get the value of this Optional instance, if present. If there is no value present in this Optional instance, then this method returns the specified value.
What is the ofNullable() method of the Optional class? The ofNullable() method is used to get an instance of the Optional class with a specified value. If the value is null , then an empty Optional object is returned. public static <T> Optional<T> ofNullable(T value)
In Java, the Optional object is a container object that may or may not contain a value. We can replace the multiple null checks using the Optional object's isPresent method. The empty method of the Optional method is used to get the empty instance of the Optional class. The returned object doesn't have any value.
Writing an Optional implementation yourself shouldn't be that hard, given the java.lang.Optional source.
Although you have to take some differences into consideration, i.e.Javascript distinguishes between undefined
and null
, you have no such concept as Generics and the Java implementation of map is considered by some as broken. Nevertheless it's fairly easy to implement.
However, I've done an implementation (optional.js) myself, which suits my needs and you may use it if you like, but it probably doesn't satisfy all your requirements (i.e. of()
does not throw an exception on undefined or null, but get()
does)
With my code, your example would be like
function getValue(key){
return Optional.of(dictionary[key]);
}
var value = getValue('key3');
value.ifPresent(function() {
console.log('got a value: ' + value.get().toUpperCase());
});
Btw. code like this
if(value.isPresent())
console.log('got a value: ' + value.get().toUpperCase());
else
console.log('no value found!');
is no valid use case for Optional as you could easily replace it will null/undefined checks. But if you require methods like filter
, map
, flatMap
and ifPresent
, you'll benefit much more for from it.
For those who still interested in this in 2021.
use the ES Optional chain always. It's more semantic and artistic in the JavaScript way (Maybe with Babel). like:
obj.val?.prop || defaultValue
obj.val?.[expr]
obj.arr?.[index]
obj.func?.(args)
Using the Optional
class it's may be NOT supported by other packages in TypeScript and if you just want to deeply access an object property is NOT kind of easy.
I try to make a roughly Optional
class with TypeScript:
import isNil from "../is/isNil";
// isNil = val => val === null || val === undefined
class Optional<T> {
value = null;
constructor(value: T) {
this.value = value;
}
static EMPTY = new Optional(null);
static empty(): Optional<unknown> {
return Optional.EMPTY;
}
static of<U>(value: U): Optional<U> {
return new Optional(value);
}
isPresent(): boolean {
return !isNil(this.value);
}
filter<T>(predicate: (value: T) => Boolean): Optional<T> {
if (!this.isPresent()) {
return this;
}
return predicate(this.value) ? this : Optional.EMPTY;
}
map<T, U>(mapper: (value: T) => U): Optional<U> {
if (!this.isPresent()) {
return this;
}
return Optional.of(mapper(this.value));
}
flatMap<T, U>(mapper: (value: T) => Optional<U>): Optional<U> {
if (!this.isPresent()) {
return this;
}
const mapped = mapper(this.value);
if (isNil(mapped)) {
throw new Error("flatMap will map the value not to null or undefined.");
}
return mapped;
}
orElse<T>(other: T): T {
return isNil(this.value) ? other : this.value;
}
}
Here you can see when I want to access property by using Optional, it will write like below:
Optional.of(dictionary)
.map((obj)=> {
const descriptors = Object.getOwnPropertyDescriptors(obj)
// ⚠️ when you use like this, it may also cause an error.
return descriptors.key1.value
})
.orElse('defaultValue')
And Using Optional chain
will like this:
dictionary?.key1 || 'defaultValue'
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With