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);
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);
}
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);
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
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!
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,
},
]
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)
|| [];
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)
Anyway how to add it to rewired app?
This works for me:
I'm sorry, I'm not sure I understand your question. What do you mean?
Nice article.
But how can I use this geature right now in Chrome without npm?