Optional chaining in Node.js 14 and JavaScript

Published on

A long awaited feature of the language and runtime has been released and is available in Node.js 14 (and modern browsers): Optional chaining!

Safe property access

Sometimes accessing properties of objects can be tedious, especially if deeply nested.

Developers often need to write exotic if-statements to verify that a given property exists.

Even worse if you need to write complex, nested ternary conditions using the ternary operator cond ? x : y.

Here is an example, adapted from code in devblog, a markdown file with attributes (parsed using front-matter):

    if (file && file.attributes && Array.isArray(file.attributes.tags)) {
      tags.push(...file.attributes.tags)
    }

Instead, now with Optional chaining you could write:

    if (Array.isArray(file?.attributes?.tags)) {
      tags.push(...file.attributes.tags)
    }

More readable (although this can be discussed) and definitely more concise!

Optional chaining, the weird parts

Syntax is syntax, you have to accept it and use it wisely to avoid being a shitty coder.

Function calls

Bare with me, this is going to look slightly weird at first.

Let’s take the above example, a markdown file that can have attributes, and the attributes has a function that determines if some tags (e.g. for a blog post) have been defined or not.

The function could be called hasTags, and we need to filter out files that have tags:

const filesWithTags = files.filter(file => file?.attributes?.hasTags?.())

Notice the ?.() part, where we first need to check if the hasTags function is available, and the “optionally call it”.

This could be an interesting use-case for optional chaining that comes into mind.

Dynamic properties

Same goes if you are using property access on an object:

const propertyName = 'attributes'
const filesWithProperty = files.filter(file => file?.[propertyName])

Using together with nullish coalescing

Read more about nullish coalescing.

An practical example could be to map all markdown files to their tags.

If a markdown files doesn’t have tags, we would like to give to default to the array ['post']:

const allTags = files.reduce((tags, file) => tags.concat(file?.attributes?.tags ?? ['post']), [])

In this way, allTags will be an array of all parsed tags in the markdown files, and if no tags have been found for a file, it uses the default tags ['post'].


For more in-depth explanation, I strongly suggest to skim through this blog post on v8.dev where Optional chaining is discussed way more in depth.

Portions of this page are modifications based on work created and shared by the V8 project and used according to terms described in the Creative Commons 3.0 Attribution License.

Here, have a slice of pizza 🍕