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.