Skip to content

About the author of Daydream Drift

Tomasz Niezgoda (LinkedIn/tomaszniezgoda & GitHub/tniezg) is the author of this blog. It contains original content written with care.

Please link back to this website when referencing any of the materials.

Author:

Optional Chaining Is Lazy

Cover image for "Optional Chaining Is Lazy".
Published

Optional chaining in JavaScript is quickly becoming the standard replacement to tools like lodash/get and long and nested comparison expressions.

// Some approaches to optional chaining.

// Lodash:
get(sourceObject, 'a[1].c')

// Native, using nullish coalescing:
;((sourceObject.a ?? {})[1] ?? {}).c

// Native optional chaining:
sourceObject.a?.[1]?.c

But don't be fooled. Like the previous approaches, it doesn't replace thinking and putting work into understanding how a software project works.

Optional chaining is commonly used as an escape from determining how things in the project actually work and what values are transferred between its parts. Using optional chaining this way can lead to difficult to debug issues in the near future and must be avoided.

Optional chaining splits source code execution into two possible paths which can split into more down the road. One path is followed when the parent value is available and the other when the previous value is unavailable. It makes code more complicated to understand. Make sure this split is indeed needed and both situations can happen. If you don't know the shape of the parent value and whether it returns optional children investigate it instead of using optional chaining. It's worth the time investment.

And definitely don't prepare for issues which might happen in the future by using optional chaining. You will end up with source code having tons of optional chains and no developer in the project will know what's going on during runtime.

const apiResponse = await fetch('https://someonlineapi.com?q=123')

const data = await apiResponse.json()

const normalizedRecords = data.map((record) => {
  return {
    id: record.id,
    title: record.atributes?.title
  }
})

// Better allow 'attributes' to be optional so this code
// doesn't fail when retrieving 'title', right? Wrong!
// Would you rather see an error on the page if
// 'attributes' doesn't exist or have the website fail
// silently and show no information to users?
// Removing optional chaining gives you the chance
// to notice missing 'attributes' quickly
// and fix the real issue.

Lastly, follow the agreed data structures. Figure out why a value breaks its assumed structure. Using optional chaining is going around the issue which will only get worse later.

const apiResponse = await fetch('https://someonlineapi.com?q=123')
const data = (await apiResponse.json()) as {
  id: string
  attributes: { title: string }
}

const title = data.attributes.title

// Referencing 'data.attributes.title' causes an error?
// Let's make it optional
// (i.e., 'data.attributes?.title'), right? Nope!
// Check what the shape of 'data' actually is.

Fail early.