2021-09-09
|~3 min read
|476 words
One of my favorite recent additions to Javascript is Optional Chaining. This behavior, which allows a program to quickly ascertain whether a property is defined at runtime and abort if it’s not means it’s now much easier to avoid attempting to access properties of something that’s ultimately undefined
.
I reach for optional chaining the most when trying to access properties on deeply nested objects.
Let’s imagine a situation where we have a customer:
const customer = {
name: {
firstName: "Stephen",
lastName: "Weiss",
},
address: {
street: "123 Main St",
streetB: "Unit 456",
city: "Chicago",
state: "IL",
},
}
Classically, if we wanted to access the firstName
property, we might do something like this:
const firstName =
customer && customer.name && customer.name.firstName
? customer.name.firstName
: undefined
With optional chaining, we can make this much more readable:
const firstName = customer?.name?.firstName // 'Stephen'
In this case, we have a firstName
and so the property is defined. What happens if you try to access this on an empty customer
object?
const customer2 = {}
const firstName = customer2?.name?.firstName // undefined
This is a significant improvement over a runtime error! Not only that, but we caught the issue early (i.e., we aborted on name
and didn’t even try to access firstName
).
One common mistake with optional chaining is to not continue the chain all the way down.
A simple rule of thumb is that once you start optional chaining, you have to continue it the whole way.
For example:
// Do this
const firstName = customer?.name?.firstName
// Do not do this
const lastName = customer?.name.lastName
Unless you can be certain that if there’s a name
, there’s always a lastName
property, the latter approach opens the application up to the runtime exception that is so easy to avoid with optional chaining.
But what about functions? It turns out they can be optionally chained too!
Imagine a function that receives an optional callback. One way to protect yourself against Undefined is not a function
is to use a if
statement to check for the callback’s presence:
type FuncProps = {
data: any
callback?: () => void
}
function myFunc(props: FuncProps) {
const { data, callback } = props
/*...process data */
if (callback) {
callback()
}
}
Alternatively, we can use optional chaining:
type FuncProps = {
data: any
callback?: () => void
}
function myFunc(props: FuncProps) {
const { data, callback } = props
/*...process data */
callback?.()
}
Optional Chaining is extremely useful and it’s now available in vanilla Javascript (ECMAScript spec)! If you haven’t been using it yet, now’s the time to start. MDN outlines several other use cases too if you’re looking for more information.
Hi there and thanks for reading! My name's Stephen. I live in Chicago with my wife, Kate, and dog, Finn. Want more? See about and get in touch!