ES2020 - Optional chaining and nullish coalescing operators

ยท 2 mins read ยท ย JavaScript

For years JavaScript engineers have been trying to protect the applications they are developing from unwanted breaking errors while working with deep nested objects.

Let's see an example. Imagine that the server-side API returns an object in JSON format about a user:

// User's object
{
  "id": 1234,
  "profile": {
    "name": "John Doe",
    "age": 25
  }
}

If we need to access this user's name in our code, we need to call user.profile.name.

const name = user.profile.name;

# Optional chaining operator

What happens though when profile key isn't provided for some reason?

// User's object
{
  "id": 1234
}

For this case if we try to access the nested key user.profile.name, our application is going to break terribly with the well-known error:

Uncaught TypeError: Cannot read proprty "name" of undefined

In order to get through this issue, we mostly try things like this:

const name = user.profile && user.profile.name;

This equals with undefined when the profile key does not exist and with the actual name when user.profile.name does exist.

Unfortunately our code is getting a little difficult to read and maintain with this approach. Imagine how ugly it will become if we have another nested object named address inside profile and we need to reach a key of the address object:

const street = user.profile &&
  user.profile.address &&
  user.profile.address.street;

This is the problem that optional chaining operator is trying to solve:

const street = user.profile?.address?.street;

We use a simple ?. at the end of an optional key in order to tell JavaScript to check carefully whether this key is not equal with null or undefined before moving on to the very next one, reading from left to right.

When an invalid one is found, then undefined is returned without further checks or fatal JavaScript exceptions. So our application is safe and isn't about to crash ๐Ÿ˜‰

# Nullish coalescing operator

So far so good!! What happens when we need to return a fallback value though?

This is something that we tackle mostly with logical OR operator:

const street =
  user.profile?.address?.street || 'Acropolis Street';

But, this is a little dangerous. Let's investigate why. Imagine this object:

// User's object
{
  "id": 1234,
  "profile": {
    "name": "John Doe",
    "age": 25,
    "address": {
      "street": "",
      "number": ""
    }
  }
}

Here the key user.profile.address.street actually exists, and it is an empty string which is fine. Unfortunately the logical OR operator will return Acropolis Street which is the fallback value but this is definitely wrong since an empty string is a valid value for our case.

Actually this is how logical OR works since the empty string evaluates to false so we need to protect ourselves from this sort of behaviour.

Below we can see some cases where the logical OR operator will return a fallback value despite the fact the ones on the left side might be considered valid ones:

// Evaluates to 'fallback value'
const myVar = '' || 'fallback value';

// Evaluates to 'fallback value'
const myVar = false || 'fallback value';

// Evaluates to 'fallback value'
const myVar = 0 || 'fallback value';

Hmm, so how do we protect ourselves from this pitfall then?

Nullish coalescing operator tries to solve this very problem. Let's see how:

const street =
  user.profile?.address?.street ?? 'Acropolis Street';

So we used ?? to provide a fallback. This operator will come into the game only when the statement on the left side returns null or undefined.

This means that optional chaining operator came across a missing key so nullish coalescing operator provided the fallback value.

Awesome combo, right?

# Bonus Material

The issues mentioned above could have been tackled by using some well-known third party libraries like Lodash and Ramda in a more elegant way.

Let's see an example with Lodash:

import { get } from 'lodash';

const street =
  get(user, 'profile.address.street', 'Acropolis Street');

And one with Ramda:

import { pathOr } from 'ramda';

const street =
  pathOr('Acropolis Street', ['profile', 'address', 'street'], user);

So these libraries could help us to overcome those problems by keeping our code a little more clean and readable instead of using logical OR operator etc

Of course these methods gained some traction the previous years since there was not an easy workaround with vanilla JavaScript. ES2020 is definitely a game-changer regarding this ๐Ÿ˜‰

Cheers!!

At the moment of this writing, nullish coalescing and optional chaining operators are in stage 4 of the process which is considered finished

You can check browsers support for optional chaining operator here and for nullish coalescing operator here

Babel offers two plugins to support optional chaining operator here and nullish coalescing operator here

TypeScript 3.7 introduced support for these operators

Newsletter

Get notified about latest posts and updates once a week!!