DEV Community

Nimmo
Nimmo

Posted on • Edited on

Optional chaining: What is it, and how can you add it to your JavaScript application right now?

This post assumes that you're already transpiling your JS applications with Babel (version 7+). If you're not, then this probably isn't the feature to convince you to add that into your build process, but it's still a proposed language feature that is worth being aware of.

You've seen these errors before, hiding in your logs, in your automated test readouts, in your console, in your devtools: cannot read property "map" of undefined.

You spend time tracking down the error, and find the function in question:

const someFunction =
  someArray => 
    someArray.map(someOtherFunction);
Enter fullscreen mode Exit fullscreen mode

You spend even more time looking at the code that called this function. Sometimes that array really might be undefined. In this scenario you decide that it is someFunction's responsibility to handle that. You update your code, and leave a helpful comment so that no-one else wastes time wondering why you're accounting for this:

const someFunction =
  (someArray) => {
    // Sometimes this is undefined: See [link to bug report]
    if (someArray === undefined) {
      return [];
    }

    return someArray.map(someOtherFunction);
  }
Enter fullscreen mode Exit fullscreen mode

This works. But, you kind of liked the implicit return from the original example. A single expression in a function makes you feel more comfortable. No way anything else can sneak in there and cause problems. I like your thinking.

You try again, with a single expression this time, using a default value:

const someFunction = 
  (someArray = []) => 
    // Sometimes this is undefined: See [link to bug report]
    someArray.map(someOtherFunction);
Enter fullscreen mode Exit fullscreen mode

This works. But, now your helpful comment is a bit weird. Will someone think that the output of this function is undefined, and accidentally account for that possibility elsewhere, even though this will always return an array? You imagine the confusion you've potentially caused, and the accumulated (hypothetical) cost to your company as a result.

You could make your comment clearer, but you want to solve this problem using JavaScript, not boring words.

You could resign yourself to a ternary, but that would mean having to type someArray an extra time. Let's look at a new alternative:

Enter optional chaining

With optional chaining, you have a new operator: ?.

You can use ?. on anything that you think might be undefined, which can save you from the most common and the most frustrating issues you see regularly in JS. For example:

const withoutOptionalChaining =
  something
    && something.someOtherThing
    && something.someOtherThing.yetAnotherThing

const withOptionalChaining =
  something
    ?.someOtherThing
    ?.yetAnotherThing
Enter fullscreen mode Exit fullscreen mode

It's crucial to understand that if either someOtherThing or yetAnotherThing are undefined, then the withoutOptionalChaining example will be false, where the withOptionalChaining example will be undefined.

As you're aware if you've written JS for anything more than a day, undefined is not a function. But, what if that didn't matter?

const someValue = 
  someObject.someFunction?.() // returns `undefined` rather than a runtime error if `someFunction` is undefined!
Enter fullscreen mode Exit fullscreen mode

 I'm in. But, how?

Fortunately, there's a Babel plugin for this: @babel/plugin-proposal-optional-chaining

Install that plugin with npm, and add it to your babel config via your chosen configuration option.

Depending on the rest of your Babel config, you may also find that you end up with an error about regenerator runtime not being defined. If so, you may need to add the @babel/plugin-transform-runtime as well, and configure it like so:

['@babel/plugin-transform-runtime',
  {
    regenerator: true,        
  },
]
Enter fullscreen mode Exit fullscreen mode

If you're using ESlint, you'll find that it isn't too happy about this new operator. You'll also need to add the babel-eslint plugin to your ESlint config.

And that's it. Now you ought to be able to use optional chaining as much as you want to in your application.

Let's look again at that original code:

const someFunction =
  someArray => 
    someArray 
      // Sometimes this is undefined: See [link to bug report]
      ?.map(someOtherFunction)
      || [];
Enter fullscreen mode Exit fullscreen mode

There we have it, another option for solving our problem. Do you always want to do this? Absolutely not: there are times when you probably do want a runtime error after all. But for the rest of the time, optional chaining is a great addition to your toolkit.

Disclaimer

Optional chaining is currently at Stage 1 in the proposal process, so whether or not you are willing to incorporate it right now is up to you.

Top comments (4)

Collapse
 
yuritoledo profile image
Yuri Toledo

Anyway how to add it to rewired app?

Collapse
 
sabarasaba profile image
Ignacio Rivas

This works for me:

const {
  override,
  addBabelPlugins,
} = require('customize-cra');

...

module.exports = override(
  ...
  addBabelPlugins('@babel/plugin-proposal-optional-chaining'),
  ...
);

Collapse
 
nimmo profile image
Nimmo

I'm sorry, I'm not sure I understand your question. What do you mean?

Collapse
 
4rontender profile image
Rinat Valiullov

Nice article.
But how can I use this geature right now in Chrome without npm?