Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JavaScript analog of java.lang.Optional?

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.

like image 205
tsayen Avatar asked Sep 01 '15 07:09

tsayen


People also ask

What is Java optional of?

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.

What is orElse in optional Java?

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 Java optional ofNullable?

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)

What is optional empty () Java?

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.


2 Answers

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.

like image 73
Gerald Mücke Avatar answered Sep 22 '22 15:09

Gerald Mücke


For those who still interested in this in 2021.

tl;dr:

  • 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.

for more info:

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'
like image 20
TommY Avatar answered Sep 22 '22 15:09

TommY