JavaScript Optional Chaining – An Introduction

Lawrence Chabela CSS & HTML, JavaScript, Problem Solving, Technology Snapshot 1 Comment

There is a new exciting feature coming to JavaScript in the not-so-far future. That feature is Optional Chaining. At this moment, Optional Chaining is in Stage 3 of the TC39 process, so it’s in late stages of the process and will be here soonish.

In general terms, Optional Chaining is an approach to simplify JavaScript expressions for accessing deeply nested values, array items and methods when there is a possibility that a reference may be missing.

In this blog, we give an introduction to Optional Chaining in JavaScript. We discuss what problems Optional Chaining solves, the various ways you can use it, and relatable code examples.

Current Problem

Consider the following object structure – it might look similar to something you may be working on.

const user = {
    name: {
        first: 'Floyd',
        last: 'Pepper'
    },

    address: {
        zip: '14221',
        state: 'MO',
        street: '456 Sesame Street'
    },

    company: {
        name: 'Acme Corporation',
        address: {
            zip: '14221',
            state: 'MO',
            street: '456 Sesame Street'
        }
    },

    contacts: [{
        type: 'email',
        value: '[email protected]'
    },{
        type: 'phone',
        value: '1234567890'
    }]
}

Something like this may come from:

  • Accessing remote data from an API
  • Some sort of configuration object in an application
  • A model with optional properties

While JavaScript gives us the flexibility for objects to take on varying shapes, this uncertainty of the shape at runtime can make accessing properties on these objects a bit complex or adding unneeded work to do so.

Take the scenario with our example user object above. Say we want to get the user’s company address. You might do something like this:

const companyAddress = user.company.address;

This is all just fine and dandy. But, as we said above, we may not be guaranteed to have all the properties at runtime for our user object that we are accessing to get the company address. If the above doesn’t have the company property, then our `user.company.address` would end up throwing an error.

Not a big deal right? You would just end up writing something like this:

// Less likely to error but harder to read, right?
const companyAddress = user && user.company && user.address;

The above uses short-circuiting by checking each node. If it’s undefined or null, it will exit instead of accessing the undefined properties. This isn’t too bad but can become very tedious and complex as our data structure grows.

// You can see what I mean, right?
const deepValue = foo && foo.bar && foo.bar.baz && foo.bar.baz.qux && foo.bar.baz.qux.bar;

That example is a bit of an extreme I know, but you can see how this can get out of hand. Even more so as the complexity of object increases and the further down into it you need to retrieve a value.

See Also:  Lean Mean Vue Machine

Optional Chaining to the Rescue

Static Property Accesses

So where does Optional Chaining come into play to help us, you say?

Let’s take our same example from above where we were trying to get the user company address.

// Nice, right?
const companyAddress = user?.company?.address;

Isn’t that so much nicer and so much less work without having to do our above checks for each property before we access a deeper one.

The above ? is doing and saying the following:

  • Check if user is not an undefined or null value.
  • If it isn’t, check if user.company is. If it isn’t, then return the value of user.company.address.
  •  If undefined is found anywhere in the chain of property drilling, we simply bail out and return undefined.

Dynamic Property Access

The great thing is that Optional Chaining can also be used to access array items and dynamic properties safely as well. Say that we want to get from our user their first contact type value. You may do:

// Ugly, right?
const contactType = 
    user.contacts && 
    Array.isArray(user.contacts) 
    && user.contacts.length 
    && user.contacts[0].type

Again, you can see how this can get out of hand fast. Let’s try that again with Optional Chaining.

const contactType = user?.contacts?.[0]?.type

user? check to see if it exists, then [0]?. makes sure that the contact exists in the contact list. Pretty slick!

Or if we want to grab a property dynamically, we can do that by simply doing the following:

const propName = 'zip'
const propValue = user?.address?.[propName];

It is a fairly simple example, but you can see the power of this approach through it.

Function or Method Call

You may be asking yourself, what else can Optional Chaining do? Well, we can also handle calling methods that may or may not exist on object.

Let’s say we have an element in the DOM that we want to retrieve and add another element to it. That element could or could not be there. What might that look like?

const parent = document.querySelector('.js-my-parent-element');
const child = document.createElement('div');

if(parent) parent.appendChild(child); 

Pretty simple stuff, but let’s spice it up with Optional Chaining.

const child = document.createElement('div');

document.querySelector('.js-my-parent-element')?.appendChild(child); 

Isn’t it nice that we no longer have to add a check for the element existing? We can simply use Optional Chaining to do that for us then call the method if it exists.

See Also:  Progressive Web App Tips and Tricks

Another cool thing is that we can combine all three of these Optional Chaining approaches if we want (or if the situation calls for it).

const value = model.property?.method()?.[0].foo;

Forms Of Optional Chaining

Let’s briefly summarize the ways you can use Optional Chaining.

Accessing static properties obj?.prop:

    const model = undefined;
    model?.someProp; // returns undefined

    const modelB = { foo: 123 };
    modelB?.foo; // returns 123

Accessing dynamic properties obj?.[expr] or an array item:

    const model = undefined;
    const propName = 'foo';

    model?.[propName]; // returns undefined

    const modelB = { bar: 'abc' };
    const propNameB = 'bar';

    modelB?.[propNameB]; // returns 'abc'

    const list = undefined;
    
    list?.[0]; // returns 'undefined'

    const listB = [0,1,2];

    listB?.[0] // returns 0

Optional function or method call func?.(...args):

    const object = undefined;
    object?.method('value'); // returns undefined

    const objectB = { method: (val) = > val };
    objectB?.method('value'); // returns 'value'

Okay, I am sold. When can I use this?

As stated above, Optional Chaining is currently in Stage 3 of the TC39 Process. So it is close to being able to be used in the browser.

Until then, you have the option of using it today via a Babel Plugin that can be added to most front end build processes.

npm install -D @babel/plugin-syntax-optional-chaining
{
   "plugins": ["@babel/plugin-syntax-optional-chaining"]
}

Optional Chaining is the Bees Knees

Optional Chaining provides us with the ease to access deeply nested values, array items and methods. It allows us to cut out the usual repetitive code we use to have to write to check for undefined or null values on each property we accessed in our chain.

Comments 1

  1. I didn’t know this was coming. I run into the issue it solves all the time! Or worse, I get lazy and then the app blows in the future.
    Thanks for the information! This article is well-done!

What Do You Think?