All Articles

JavaScript optional chaining

ES2019

Optional property chaining, a welcomed proposal by many developers, has been moved to stage 3 which means it’s a candidate for getting added to the ECMAScript standard. Once it hits stage 4 it’s on the waiting list to be added in the next iteration.

Nested properties

The flexibility of JavaScript sometimes comes at a cost as our objects don’t have a strictly defined structure. When we are working with certain API we often get objects missing an expected property or even the whole object being null.
So if one of our objects doesn’t have the desired property and we try to access it we get the familiar error:

const user = {
  address: {
    // city: {
    //   name: "London",
    //   code: 20
    // }
  },
  firstName: "Jane",
  lastName: "Doe",
  getFullName() {
    return `${this.firstName} ${this.lastName}`;
  }
};

const cityCode = user.address.city.code; // Uncaught TypeError: Cannot read property code of undefined

Workarounds

To check if any of the members in the property chain exist we use workarounds like these:

const cityCode1 = user.address && user.address.city && user.address.city.code;

const cityCode2 = (((user || {}).address || {}).city || {}).code;

try {
  const cityCode3 = user.address.city.code;
} catch (e) {}

Or using some of the popular libraries like Lodash:

import _ from "lodash";

_.get(cityCode, "user.address.city.code");

Or Ramda:

import R from "ramda";

R.path(["user", "address", "city", "code"], cityCode);

Optional chaining

This is where the optional chaining operator ?. comes in and basically checks whatever is on its left side. If the value is null or undefined it short-circuits and returns the value of undefined. If not it just continues the evaluation.
This enables safely referencing deeply nested object properties even if they don’t exist. We just chain the optional checks down to the property we want to use:

const user = {
  address: {
    city: {
      name: "London"
      //code: 20
    }
  },
  firstName: "Jane",
  lastName: "Doe",
  getFullName() {
    return `${this.firstName} ${this.lastName}`;
  }
};

user?.address?.city?.code; // undefined

Using the bracket notation we can reference dynamically added properties:

let prop = "address";

user?.[prop]?.city?.name; // “London”

We can even call functions that may not exist:

user?.getFullName?.(); // "Jane Doe"

And also access items in an array:

const arr = ["Jane", "Mark", "Joan"];

const secondItem = arr?.item?.[1]; // "Mark"

Or delete properties:

delete user?.address?.city?.code;

Babel plugin

This feature has long been a part of many languages like C#, Groovy, and Swift and it’s finally coming to JavaScript in the near future. If you, however, want to start using it right now, the folks at Babel got you covered with their plugin for the proposal. Just make sure you use Babel7 and install the plugin:

npm install --save-dev @babel/plugin-proposal-optional-chaining

Then add it to your .babelrc configuration:

{
  "plugins": ["@babel/plugin-proposal-optional-chaining"]
}

Like what you've read? Join our newsletter

Launching a new JavaScript project? Need help on an existing project? Work with us