Hi everyone! It’s another edition of Boring JavaScript. So trash those sleeping pills – you won’t need them.
This post looks at “Optional Chaining” in JavaScript.
Never heard of it?
Not surprising. It’s a rather new addition to JavaScript that only recently has been implemented by real browsers. It does two things very well:
- Cleans up your code and makes it more readable and maintainable.
- Gracefully gets rid of those unsightly and out-of-the-blue “Cannot read property ‘myProperty’ of undefined” – yes, the dreaded “TypeError”.
What Problem are We Trying to Solve?
Take a look at this code:
const animals = [
{
type: "Coyote",
name: "Wile E.",
stats: {
height: "0.67 m",
weight: "20.86 kg",
speed: "64.37 kmh",
intelligence: "Super Genius"
},
habitat: {
location: "North America - South West"
}
},
{
type: "Cat",
name: "Fluffy",
fur: "Soft",
stats: {
height: "0.23 m",
weight: "4.08 kg",
speed: "40.23 kmh",
intelligence: "Genius"
}
}
]
const coyoteFur = animals[0].fur;
console.log(`My coyote fur is ${coyoteFur}`);
const catHabitat = animals[1].habitat.location;
console.log(`My cat habitat is ${catHabitat}`);
If you run this code, you’d get the following:
TypeError: Cannot read property 'location' of undefined
Why? If you notice in the two objects ‘coyote’ and ‘cat’, there are properties on some of the objects but not the others. This is perfectly normal – especially if you are dealing with NoSQL databases like MongoDB. It is perfectly natural to only save the data you need for each record. In the case above, we have ‘habitat’ data for the Coyote, and ‘fur’ data for the Cat.
If you have an ‘undefined’ object, JavaScript cannot find a property on it because – well – it’s undefined. For the ‘animals[0].fur’ variable, it returned ‘undefined’ because it couldn’t find ‘fur’ on a defined variable, that being ‘animals[0]’.
However, the ‘catHabitat’ line threw an exception. JavaScript tried to find the property ‘location’ on the property ‘habitat’, but ‘habitat’ doesn’t exist for ‘animals[1]’ – meaning, ‘habitat’ is undefined. And you can’t find a property on an undefined property, so JavaScript threw the error.
Enter Optional Chaining.
Let’s look at the last four lines again, and make a change in the third line:
const coyoteFur = animals[0].fur;
console.log(`My coyote fur is ${coyoteFur}`);
const catHabitat = animals[1].habitat?.location;
console.log(`My cat habitat is ${catHabitat}`);
Do you see the change? It’s small. It’s tiny. But it’s an important change. I’ll bold it in red here:
const catHabitat = animals[1].habitat?.location;
That “?.” after “habitat” and before “location” is called Optional Chaining. If the property preceding the “?.” is undefined, then the rest of the statement is ignored and the value returned is undefined. Which means that you get the value you intended – that is undefined – and you don’t have to worry about JavaScript throwing that ugly TypeError. You code is cleaner, easier to understand, and is more maintainable.
What will the code have to look like if we didn’t use Optional Chaining? Like this:
const catHabitat = animals[1].habitat ? animals[1].habitat.location ? animals[1].habitat.location : undefinded : undefined;
Or…
let catHabitat = undefined;
try {
catHabitat = animals[1].habitat.location;
} catch (err) {
catHabitat = undefined;
}
Or…
let catHabitat = undefined;
if (animals[1].habitat && animals[1].habitat.location) {
catHabitat = animals[1].habitat.location;
}
Pretty ugly.
As you can see, Optional Chaining makes your code easier to read and provides a safe, convenient method of gracefully doing away with that nasty TypeError.
And it can get bad without Optional Chaining
Consider this real world scenario:
I have a database of animals, and I want you to get me the Low Elevation reading for each animal in which the animal’s habitat is in Zone A1. Of course, not all animals will have a Low Elevation reading in Zone A1 of their habitat – I mean, some animals may not even have a habitat property.
In the old days, this is what you had to do:
let reading = undefined;
if (animal && animal.habitat && animal.habitat.zone && animal.habitat.zone.A1 && animal.habitat.zone.A1.elevations && animal.habitat.zone.A1.elevations.low) {
reading = animal.habitat.zone.A1.elevations.low;
}
Now all that mess can be written using Optional Chaining.
let reading = animal?.habitat?.zone?.A1?.elevations?.low;
That is the magic and beauty of Optional Chaining.
And now the video
Make sure you subscribe to our YouTube channel, or go https://www.boringjavascript.com for a listing of all our videos.
I’ll see you later!
Categories: Boring JavaScript Javascript
thevirtuoid
Web Tinkerer. No, not like Tinkerbell.
Creator of the game Virtuoid. Boring JavaScript. Visit us at thevirtuoid.com
Leave a Reply