ponyfoo.com

Null Propagation Operator in JavaScript

Fix
A relevant ad will be displayed here soon. These ads help pay for my hosting.
Please consider disabling your ad blocker on Pony Foo. These ads help pay for my hosting.
You can support Pony Foo directly through Patreon or via PayPal.

There’s a proposal in stage 1 for the Null Propagation operator. In this article we’ll take a look at the proposal, which offers an alternative to null checks ad nauseum.

Very often, we want to grab a value deeply, such as in the following bit of code.

const firstName = person.profile.name.firstName

The profile might not be an object. Or the name might not be an object. So we null-check all the things!

const firstName = (
  person &&
  person.profile &&
  person.profile.name &&
  person.profile.name.firstName
)

Or we use a library like lodash to do the checking for us, at least there’s less unwarranted complexity on our own bits of code.

import { get } from 'lodash'
const firstName = get(person, ['profile', 'name', 'firstName'])

The Null Propagation operator is a native solution to the problem, allowing us to handle these cases by sprinkling our code with question marks. The operator is all of ?., as we’ll see in a bit.

const firstName = person.profile?.name?.firstName

Note that the ?. goes right before the property access. We can think of each ?. operator as a short circuit where "if the expression up until this point is null or undefined, then the whole expression evaluates to undefined".

const read = person => person.profile?.name?.firstName
read() // <- Error, because `person` is undefined
read({}) // <- undefined, because of `profile?.`
read({ profile: {} }) // <- undefined, because of `name?.`
read({ profile: { name: {} } }) // <- undefined, because `firstName` is undefined
read({ profile: { name: { firstName: 'Bob' } } }) // <- 'Bob'

The operator can come after any expression, including function calls. In the following example we run a regular expression against a string, and if it matches we get back the matched group. Note that even though we’re using the object property access expression notation, we have to use ?.[expression] and can’t just use ?[expression]. This allows the compilers to disambiguate the grammar more easily.

/(\d+)/.exec('abcdef')?.[1] // <- undefined
/(\d+)/.exec('abc1234def')?.[1] // <- '1234'

Using Null Propagation, we could also optionally call functions. In the following example, we have the person eat some foods, provided a person.eat method exists. Again, the operator remains ?. to ease the burden on lexical analyzers.

person.eat?.(carrot, pasta, apple)

If we go back to the earlier example of reading a person’s name, and assuming that names are in the form 'First Last', we could do the following to get each part of their name, but only if they indeed have a name and only if the name property value may be split.

const read = person => person.name?.split?.(' ')
read({}) // <- undefined, because `name` doesn't exist
read({ name: 33 }) // <- undefined, because `33` doesn't have a `split` method
read({ name: 'Uncle Bob' }) // <- ['Uncle', 'Bob']

Probably the least useful bit of the proposal is optional constructor invocation, shown in the next snippet. That said, it’s a good idea to include this in the proposal as to avoid the drama that came with new + apply prior to the rest operator*.

new Carriage?.(horses)

The proposal also discusses write context, that is, using the null propagation operator ?. while writing or deleting properties. These kinds of use cases rarely pop up in the wild, so the proposal probably will end up not covering them.

person?.firstName = 'Bob' // only carried out if `person` is not null or undefined
delete person?.lastName // only carried out if `person` is not null or undefined

You can find the proposal document on GitHub.

* The rest operator introduced a clean new Date(...[2017, 6, 17]) syntax. In the fun old days, doing new and apply on a constructor involved lot more fun stuff than that: new (Date.bind.apply(Date, [null, 2017, 6, 17])).

Liked the article? Subscribe below to get an email when new articles come out! Also, follow @ponyfoo on Twitter and @ponyfoo on Facebook.
One-click unsubscribe, anytime. Learn more.

Comments