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]))
.
Comments (25)
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
andnull.anything
to returnundefined
, so that you can chainfoo.bar.baz
normally and getundefined
if a property doesn’t exist along the chain. Does that kind of solution do too much to break backwards compatibility?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.
I agree it would be preferable to avoid the additional syntax, and simply return undefined similar to how the Lodash .get method behaves.
What are browsers supported Null propagation operator?, I didn’t find something about in mozilla developer guide, and Chrome doesn’t support it.
This is just a proposal. It might take a long time until it gets to browsers. Read these for more details:
+1 - give me this plus implicit function returns and that would cover a large chunk of the remaining reasons to prefer CoffeeScript
Should be mentioned that in other programming languages it’s known as “Safe navigation operator”, see: https://en.wikipedia.org/wiki/Safe_navigation_operator
Awesome!!! That would be incredible helpful.
So, I wonder if we’re going to get a special symbol property for allowing the NPO to dispatch to a custom function?
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 toobj?.prop
, it is pretty cool, efficient and even semantically understandable, but transformingobj.method(params)
toobj.method?.(params)
orarray[index]
toarray?.[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 wrotearray?.[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.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.Discovered in a PR: https://github.com/alexa/skill-sample-nodejs-audio-player/pull/4
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?This is a great improvment, yhey should have came up with this in ES 5.1.
IIRC, all of ES5/5.1 is available via polyfill… syntax/structure changes can’t be polyfilled. ES6/2015+ added structural changes that couldn’t be filled. Though I kind of wish it was here earlier.
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?
I had similar questions regarding making unicode detection work as would be expected in the likes of regular expressions, or changing the expected string structure to UTF8 under the covers. I feel it would fix more than it broke, but changing behavior in an incompatible way is VERY strongly avoided… and if you look at, for example, how long Python2 has hung on vs. 3, I can see why.
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 ‘??’ … ?
In C# ?. has been around for a few years and now I can’t live without it. One thing that is not too obvious is that when implemented in a language it also needs the ?? as well to be really efficient.
Imagine
var e = a?.b?.c?.d;
now what to do if e is undefined?
if(e == undefined) e = fallback;
then we have do do a undefined-check anyway (i.e. annoying)
the solution is
var e = a?.b?.c?.d ?? fallback;
which is why ?? should be implemented as well.
?? is implemented as || in an assignment in JavaScript
FYI PHP 7 introduced Null coalescing operator with syntax like
$foo ?? $bar ?? "fallback"
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.
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
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.
C# has both…