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
undefined
is 🙅🏽♂️ - 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 log
ging 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)