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

const read = person => person.name?.slice?.(' ')
read({}) // <- undefined, because `name` doesn't exist
read({ name: 33 }) // <- undefined, because `33` doesn't have a `slice` 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 (20)

Ryan D. wrote

I would be interested in an explanation for why we need new syntax for this. The solution I would love would be for undefined.anything and null.anything to return undefined, so that you can chain foo.bar.baz normally and get undefined if a property doesn’t exist along the chain. Does that kind of solution do too much to break backwards compatibility?

Nicolás Bevacqua wrote

Of course. Websites are written in all kinds of different “coding styles”, purposely running into exceptions is one of those. Any site that intentionally runs into exceptions would be broken under your proposal. In the web, backward compatibility is crucial.

Don Payne wrote

I agree it would be preferable to avoid the additional syntax, and simply return undefined similar to how the Lodash .get method behaves.

Matt Kunze wrote

+1 - give me this plus implicit function returns and that would cover a large chunk of the remaining reasons to prefer CoffeeScript

Jos de Jong wrote

Awesome!!! That would be incredible helpful.

Brian wrote

So, I wonder if we’re going to get a special symbol property for allowing the NPO to dispatch to a custom function?

yogibimbi wrote

Hmm, the function part feels a bit strange, as the parentheses would not naturally come after a dot. In obj.prop it makes sense to transform it to obj?.prop, it is pretty cool, efficient and even semantically understandable, but transforming obj.method(params) to obj.method?.(params) or array[index] to array?.[index] doesn’t. I get that it is nice for the compiler, but, in the case of arrays, for example, a dot means the same as the brackets, so, semantically, you would double the brackets if you wrote array?.[index]. Semantically, the character after the ? should determine the nature of what follows. If it is a dot, what follows is a property, if it is parentheses, what follows is function parameters, and for brackets it’s an array index. Adding the dot would make it semantically confusing imho.

James Edward Lewis II wrote

I believe the distinction between putting the ?. before the method name and after it is that before, it just checks whether the named member exists, and after, it checks whether the named member is callable; similarly, a ?. between a function name and its argument list is for checking whether the named variable is callable before trying to call it.

Harry wrote

I’m not sure if the syntax for function is as simple as it could be. In coffeescript it is function?() (without the dot). Wonder why the dot anyway? Logically ? can be viewed as some sort of the polymorphic (higher order) operator, whist . and () are the possible parameters it applies to?

Vadim wrote

There is a good question do we need ?. or we can use it implicit. So instead of foo?.bar?.baz we can just have foo.bar.baz And a quesion - do we really break the web in this case? Is it a lot of code which behavior depend of this?

Codacoder wrote

This is a fab proposal. However, I’d prefer ‘?’ or perhaps ‘??’. The ‘?.’ combination kinda works for property tests but in the case of functions/arrays, looks and feels awkward. So why not just ‘?’ or ‘??’ … ?

AJ wrote

Has that point come yet?

You know that point where javascript admits that, syntactically (love that word, btw) speaking, all they are doing is slowing adopting language features that Coffeescript users have been using for years?

I will never understand how more people didn’t discover how much better of a syntax CS is over JS. The fact that a person who has never written a line of code can look at it and parse its meaning tell you all you need to know.

Nika wrote

This is great feature

However I agree that other than property tests “?.” does really look awkward, I think implicit version needs more discussion and I strongly believe property tests and property assignments should be symmetrical

Andreas Schöller wrote

Such proposals come and go on a yearly basis. But none of the previous proposals reached stae-4 and i assume none will ever. Thats because implementin the safe-navigation-operator clashes with the beloved ternary-operator. Looking at the former discussions it seems one cannot have both in one language. As e.g. Coffeescript does not support the ternary-operator it can have a safe-navigation-operator.