Last week we talked about Nullish Coalescing, now it's time for another new addition to ECMAScript.
The dreaded existence check
Have you ever worked with an API full of objects that were inconsistently formed? Multiple levels deep and frustrating to parse. Let's use a smaller example to talk about this.
const obj = {
node : {
thing: 1
}
}
Suppose we want to access thing
. We can do it like this.
const thing = obj.node.thing
But what if we can't guarantee that node
exists? If we try and access thing
on undefined
we'll trigger an error. So we'll have to check first.
const thing = obj.node ? obj.node.thing : undefined
This is the terser option, using a ternary, but it works all the same.
This is a reasonable piece of code! But it can get incredibly repetitive if you have deeply nested objects.
Optional Chaining to the Rescue
Well, we don't have to do that anymore. We can use the new optional chaining syntax.
const thing = obj.node?.thing
In this case, obj.node
is undefined
. Typically, trying to access thing
on undefined
would trigger an error, but not in this case! When you use optional chaining it doesn't throw that error and instead evaluates the expression to undefined
.
And what's even cooler is that we can combine this with nullish coalescing.
const obj = {}
const thing = obj.node?.thing ?? 2
// thing will be 2
Since using optional chaining gives us undefined
, thing
will resolve to the value on the right-hand side of the ??
operator. In this case, that's 2
.
Chaining Optional Chaining
Note that my example above uses optional chaining once in order to clarify how it works. However, you can use it multiple times in the same expression.
const thing = obj?.node?.thing
The expression above is valid and may come in handy if obj
looks like this.
const obj = null
The risks
Now I can't write about optional chaining without including a section on warnings. To be honest, a lot of people were really against adding this to the language. They had concerns about abuse, and that's fair!
?.
should NOT replace all instances of .
. If you do that you'll create all kinds of silent failures. Optional chaining is another tool in your belt, that's it.
If you don't control the data you're accessing and it's particularly nested and it's ok if the result doesn't exist then maybe optional chaining is the right choice! But notice all those "and"s in the previous run-on sentence. Make sure you explicitly choose to use this syntax. It shouldn't be your default.
Not just for objects!
Oh, I forgot to mention the best part. Optional chaining works on more than just objects!
It works on arrays.
const tenthItem = arr?.[10]
This makes sure that arr
exists before trying to access the 10th element.
It works for function calls.
const message = obj?.stringFunction()
This makes sure obj
exists before you try and call a function on it.
And it works with top-level objects.
functionDoesNotExist?.()
If this function doesn't exist, it will evaluate to undefined
.
Isn't this fun?
So much power! But remember, that means you have a responsibility to use it wisely!