Before we start to understand optional chaining, we should understand undefined and what the motivation behind optional chaining is.
undefined
const me = {
name: "CodeFinity",
age: 300,
}
console.log(me.handles); // undefined
With our composite data types - collections such as object literals 👆🏽 - if access a 🔑 that doesn't exist we get back a special primitive data type, undefined.
Yea, it's a bit weird that JS has its own 'special data type' and even weirder that we have yet another primitive data type, null (we'll deal with that in the next post in this series).
Nevertheless, hopefully at this point you can accept that the handles is a key that has 'no definition' within the context of me👆🏽.
At this point, JS is not 'error-ing out' - it's 🙆🏽♂️ with undefined
Now, referencing me 👆🏽 once again, what if we did: console.log(me.handles.twitter)❓
Uncaught TypeError: Cannot read property 'twitter' of undefined
Observations
- Accessing a 🔑 within an object literal that doesn't exist is 🙆🏽♂️ - it's
undefined. - Accessing a 🔑 on something that is
undefinedis 🙅🏽♂️ - it creates an error❗
undefined is its own primitive data type. It's not a collection type. Therefore anytime that we invoke . on undefined, JS is going to have a problem with that. By definition, primitive data types are discrete values; they cannot hold any 🔑s! That's what that error message is telling us 👆🏽.
Preventing Our Program From Crashing 😑
Referencing again, the attempt to access: me.handles.twitter 👆🏽, without optional chaining, I might have to write my code like this one:
// Referencing 'me' 👆🏽
if (me.handles) {
console.log(me.handles.twitter)
}
Now, my program will not crash because we will never reach the line: console.log(me.handles.twitter).
Instead, JS will apply implicit coercion to: me.handles. This just means that since we are using if, 'under the hood,' JS will look at the undefined value that stems from me.handles and will 'coerce' it to false (it's a 'false-y' value). So, that code inside of the { after that if will not run.
Short-Circuit && Approach
We could also do this by _short circuiting &&: me.handles && console.log(me.handles.twitter).
This time, when me.handles gets' implicitly coerced to false the right-hand side operand of && will never get run 🍦.
Ternary Approach
We could also shorten that code by using a ternary:
// Referencing 'me' 👆🏽
console.log(me.handles ? me.handles.twitter : "");
JS would again, implicitly coerce me.handles to false and would take the right hand side operand of the : operator, "", thereby logging that empty string.
Use Optional Chaining - ?. - to Prevent Our Program From Crashing 🤓
console.log(me.handles?.twitter)
This syntax sort of applies the ternary, but in a simpler way. That . after ? is the 'optional' part of this.
First, we are asking the a ❓, "Hey, JS, does me.handles come back as an object literal?" If so, then go ahead with this next part of my chain. If not, please don't freak out 😱...let's just leave it as undefined and move on.
Examples
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Optional_chaining
const adventurer = {
name: 'Alice',
cat: {
name: 'Dinah'
}
};
const dogName = adventurer.dog?.name;
console.log(dogName);
// expected output: undefined
console.log(adventurer.someNonExistentMethod?.());
// expected output: undefined
Yes, this works for methods also: console.log(adventurer.someNonExistentMethod?.());
Here's another example from that same MDN link (slightly modified):
const customer = {
name: "Carl",
details: {
age: 82,
location: "Paradise Falls" // detailed address is unknown
}
};
const customerCity = customer.details?.address?.city;
From this one, we see that we can chain optional chaining.
⚠️
I should mention that optional chaining is an ES2020 thing. This means that unless you are using something like Babel as part of your build process, you will probably not be able to use this feature in your code today. For example, a Node.js repl.it will freak out about this. However, Chrome Dev Tool's console can use optional chaining.
Top comments (0)