ESNext: JavaScript “Optional Chaining Operator”

UPDATE December 2019: This feature has now advanced to Stage-4 and will be part of the ES2020 Specification! 🎉

UPDATE: The proposed operator got renamed from “Null Propagation Operator” to “Optional Chaining Operator”. This post has been updated to include this change.

Earlier today I was delighted to see that the “Optional Chaining Operator” in JavaScript has entered stage-1. This little gem – which I know from Coffeescript – is a real timesaver, and is something I’ve been looking forward to.

Stage-1?

💁‍♂️ The TC39 Committee has a 5 stage process in place, ranging from stage-0 to stage-4, by which it develops a new language feature. Stage-1 is the proposal phase.

The problem

Let’s start at the beginning. Say we have this simple Object named message:

const message = {
  body: {
    user: {
      firstName: 'Bramus',
      lastName: 'Van Damme',
    },
  },
};

To access the firstName property of that object, we can use this snippet right here:

const firstName = message.body.user.firstName;

However, this isn’t really safe: if message.body.user were not to exist, we’d get a JavaScript error:

const thisWillFail = message.body.inexistent.firstName;
// --> "Cannot read property 'firstName' of undefined"

Uh oh!

The hacky workaround

To fix this we – at this moment – can reside to Short-Circuit Evaluation:

const firstName = message.body && message.body.user && message.body.user.firstName;

Shouldn’t that return true?

💁‍♂️ The reason that this works it because of JavaScript returning the last encountered value – and not a boolean – when using Short-Circuit Logic.

To include a default fallback value, we can add some more Short-Circuit Logic, using the || operator:

const firstName = (message.body && message.body.user && message.body.user.firstName) || 'Stranger';

If the expression to the left of the || operator were to evaluate to something falsy (e.g. null, undefined, false, 0, empty string, …), the value to the right will be used.

Alternatively one could also use something like this:

const firstName = (((message || {}).body || {}).user || {}).firstName || 'Stranger';

(… Yeah, I know!?)

The proper solution

Now enter the “Optional Chaining Operator” ?. which provides us with a very elegant syntax and fail-safe approach to the issue described above:

const firstName = message.body?.user?.firstName || 'Stranger';

Whenever the expression to the left of the ?. operator evaluates to either null or undefined the whole expression it belongs to will evaluate to undefined, thus falling back to the default defined using the ||:

const firstName = message.body?.inexistent?.firstName || 'Stranger';

In the example above message.body?.inexistent evaluates to undefined. Therefore the entire message.body?.inexistent?.firstName expression will be considered to be undefined.

As a result the interpreter will then evaluate const firstName = undefined || 'Stranger';, to finally return 'Stranger' as the value for firstName.

Here’s to hoping that this one reaches stage-4 any time soon. As far as I can tell this is not in Babel (yet) the Babel folks are working on it.

ECMAScript Proposal: Optional Chaining →

💻 The examples embedded in this post are part of a talk on ESNext named “What’s next
 for JavaScript?”, which I gave at a Fronteers België meetup and Frontend United 2018 (Utrecht). You can check the slides / a recording out here. I’m available for bringing this talk at your meetup/conference.

Did this help you out? Like what you see?
Consider donating.

I don’t run ads on my blog nor do I do this for profit. A donation however would always put a smile on my face though. Thanks!

☕️ Buy me a Coffee ($3)

Join the Conversation

8 Comments

  1. Meanwhile the php can do

    const firstname = message.body.user.firstname ?? ‘default‘;

    (it’s php functionalty written in js format)

  2. Uhm that would still not work you if your chain failed to deliver you a user object. This is about chaining and not about simple null coalesce operators.

  3. Better could be notation like this:

    const firstName = &message.body.user.firstName || ‘Stranger’

    It is not very safe to guard each field of the chain. What if you forget some “?”

    Or may be allow some like this construction

    const firstName = try{message.body.user.firstName}.catch(‘Stranger’)

Leave a comment

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.