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.
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 anundefined
ornull
value. - If it isn’t, check if
user.company
is. If it isn’t, then return the value ofuser.company.address
. - If
undefined
is found anywhere in the chain of property drilling, we simply bail out and returnundefined
.
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.
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.