Why You Should Be Using Optional Chaining

Say goodbye to ugly ternary operator chains

Jordan McRae's profile picture

Jordan McRae

Full Stack Javascript Developer

ยท 5 min read


If you've been writing JavaScript for some time, you've undoubtedly run into situations where you have some deeply nested JSON data and need to drill down to get what you're looking for. What happens if the nested data you're looking for doesn't exist? What happens if your data structure is dynamic? You'll run into ugly situations where you need to protect yourself from the dreaded

cannot read property * of undefined
.

Let's start by taking a look at an example of how we would currently do this.

A progressive example

const users = [ { name: 'Susan', car : { brand : 'Ford', model : 'Fusion', colour : 'red', } }, { name : 'Fred', car : null, } ]

We have two users, Susan and Fred. Susan has purchased a car, and her car details are inside of our database. Fred, on the other hand, has not purchased a car so there are no details about his car.

We want to loop through all of the users in our array and log out all of the car colours. It would look something like this:

const users = [ ... ]; users.forEach(user => { console.log(user.car.color); });

We would see the first vehicle colour, red, logged to the console, but our loop would error out on Fred's car since

car.color
will not exist.

We would traditionally handle this by using a conditional operator:

const users = [ ... ]; users.forEach(user => { console.log(user.car ? user.car.colour : 'none'); });

This works well, but what happens if we have nested purchase details like this?

const users = [ { name: 'Susan', car : { brand : 'Ford', model : 'Fusion', colour : 'red', purchaseDetails : { date : '01/01/2020', purchasePrice : 5000.00, } } }, { name : 'Fred', car : null, } ]

Now our data structure is significantly more complex. If we wanted to log out the purchase price, but also account for Fred not owning a vehicle, we could do something like the following.

const users = [ ... ]; users.forEach(user => { console.log(user.car ? (user.car.purchaseDetails ? user.car.purchaseDetails.purchasePrice : 'none') : 'none'); });

Not too readable or fun do debug, is it? What happens if we have yet another nested object? You can see how quickly overcomplicated this becomes for such a trivial task. Let's explore conditional chaining and how you can start using it today.

What is optional chaining?

Optional chaining is an incredibly powerful tool that allows you to optionally select portions of data without fear of your code erroring out because of dynamic data structures. It also lets you skip the messy syntax we saw above. Optional chaining is still in the proposal stage, but that doesn't mean you can't start using it. You can read more about the official spec proposal here.

Building off of what we saw above, let's jump into an example to see exactly how it works. More details can be found at the bottom of this article about how to get your project up and running with optional chaining as it will not work in all browsers as of February 2020.

Optional Chaining Examples

Take a look at the following data structure again:

const users = [ { name: 'Susan', car : { brand : 'Ford', model : 'Fusion', colour : 'red', } }, { name : 'Fred', car : null, } ]

With optional chaining, you don't have to worry about Fred not owning a car. You could easily accomplish what we tried above with the following code:

const users = [ ... ]; users.forEach(user => { console.log(user?.car?.colour || 'none'); });

Notice how we now have a leading

?
before each of the nested properties? If at any point in the chain something doesn't exist, the chain will return
undefined
. This makes it super easy to catch edge cases.

Take a look at the more complicated data again:

const users = [ { name: 'Susan', car : { brand : 'Ford', model : 'Fusion', colour : 'red', purchaseDetails : { date : '01/01/2020', purchasePrice : 5000.00, } } }, { name : 'Fred', car : null, } ]

Logging out the car purchase prices would now look like this:

const users = [ ... ]; users.forEach(user => { console.log(user?.car?.purchaseDetails?.purchasePrice || 'none'); });

How cool is that? We've now protected ourselves from unreadable code, unwanted side-effects, and we can greatly improve our mental workflow without having to worry about the overhead of organizing ternary operator chains.

Other Uses

This is only scratching the surface of what you can do with optional chaining. You aren't limited to just nested data structures. Are you unsure if something is actually a function? Check this out:

const methodOne = () => { return 'Hello'; } let methodTwo; methodOne?.(); // Returns 'Hello' methodTwo?.(); // Returns undefined

Setting Up Your Project

In order to use optional chaining, you will need to use the Babel proposal plugin until it is officially included in the JavaScript spec.

To get started, run:

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

Inside of your

.babelrc
file, make sure to include the following in your plugins:

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

And you should be all set! Optional chaining is quickly moving through the TC39 proposal pipeline, so it will likely be supported across browsers very soon. If taking a deep dive into the spec is something that interests you, check it out here.


JavaScript
TC39
Share

Stack Five is a React and NodeJS consulting company that strives to push the boundaries of the web and build meaningful things. If you're looking to create a cutting-edge web application or need software engineering resources for your project, please contact us.

Stack Five logo

2020 Stack Five Inc. | Toronto, Canada