javascript: optional chaining

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.

Optional Chaining Property Accessors

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).

Pitfalls: Incomplete Chains

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.

Optional Chaining Function Calls

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?.()
}

Wrap Up

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!