DEV Community

Sammy Israwi
Sammy Israwi

Posted on • Updated on

Optional Chaining may be coming to JavaScript

Update: Optional Chaining is finally in Stage 4!! Which means it is part of the ES2020 specification. It has also already made it to TypeScript v3.7.

Here's the repo with all the info you need.

What is it?

Optional Chaining allows us to check if an object exists before trying to access its properties. Some other languages have something similar. C#, for example, has a Null Conditional Operator that behaves very similarly as the proposed Optional Chaining.

Why do we need it?

Have you ever had to check for the existence of objects or arrays before accessing its properties? If you forget, it may look a little bit like this:

if(specimen && specimen.arms && specimen.arms.length > 2)
    console.log("This is probably an alien");
Enter fullscreen mode Exit fullscreen mode

The reason why we do these checks is because in JavaScript allows for anonymous objects that don't necessarily have a structure or schema. Therefore if we don't check for parents in an object tree, we get a lot of errors that look like this:

TypeError error message

Because, at least in the case of the error, specimen does exist, but it does not have an arms property. Therefore, we tried to get length of something that was undefined.

So, what's the proposal?

Instead of all that, we can chain optional checks like this:

if(specimen?.arms?.length > 2)
    console.log("This is probably an alien");
Enter fullscreen mode Exit fullscreen mode

However, keep in mind that the Optional Chaining operator is ?. not ? - this means that when using it to access an item in an array, it will look like this instead:

var firstArm = specimen?.arms?.[0]; //CORRECT
var secondArm = specimen?.arms?[1]; //WRONG
Enter fullscreen mode Exit fullscreen mode

Similarly, when using it to check for the existence of functions:

var kickPromise = specimen?.kick?.();  //CORRECT
var punchPromise = specimen?.punch?(); //WRONG

Enter fullscreen mode Exit fullscreen mode

In this case, we check if kick exists before calling it as a function!

How does it work?

The operator checks if whatever is to the Left-Hand Side of ?. is null or undefined. If it is, then the expression short-circuits and returns undefined. Otherwise, the expression continues to evaluate as if nothing is wrong.

When can I use it?

Well, it is still a proposal so it's not in Vanilla JavaScript just yet. However, it is usable with Babel!

To stay up to date with the status of the proposal, as well as to have a more in-depth understanding and to check some examples, you should check their GitHub repo! You can also find the specs here, but I won't get into that since most of that document goes way over my head 馃槄

Thank you!

You're welcome! 鉂わ笍

Discussion (41)

Collapse
bsotodo profile image
Brahian E. Soto Mercedes

I don't know about the syntax (func?.() and arr?.[0] is kind of weird) but it would be really useful!

Collapse
sammyisa profile image
Sammy Israwi Author

Yeah, you are not alone in that. That syntax of those seems to be the main reason this is still in Stage 1. It's likely that will change before hitting Stage 2!

Collapse
denvercoder profile image
Tim Myers

JS is looking more like Swift every day. 馃憤

Collapse
twof profile image
Alex Reilly

Swift masterrace

Collapse
wahabshah23 profile image
Abdul Wahab Shah

Typescript already has this. Would be nice in vanilla Js too

Collapse
neoscrib profile image
Tanner Jepsen

Typescript does not have it. They won't implement anything in stage 1. github.com/Microsoft/TypeScript/is...

Collapse
evanderwpmorais profile image
Evander

I have used it in Angular isn't it Typescript?

Thread Thread
cristianfalcone profile image
Cristian Falcone

That's from Angular template syntax. It's called safe navigation operator. Not from TypeScript.

Collapse
sammyisa profile image
Sammy Israwi Author • Edited on

So apparently there has been a lengthy, 3 years long discussion about adding this to TS, which sometimes gets heated.

But it seems from the last comment on the thread that it actually has not been implemented in TypeScript, and it wont be implemented until the ES committee locks down its semantics :/ That is, unless there's something I'm missing?

Collapse
rapasoft profile image
Pavol Rajzak

It is present in CoffeeScript

Collapse
t4rzsan profile image
Jakob Christensen

In C# it is also called the Elvis-operator because it looks a bit like two eyes under curvy hair.

Collapse
leebradley profile image
Leopold Bradley

Very useful feature, and better than including lodash get in my code. Although, hasn't the optional chaining operator has been in Stage 1 for a while? Is there something to indicate this feature is moving up in the world?

Collapse
sammyisa profile image
Sammy Israwi Author

Yeah, it has been in Stage 1 for a long while. From what I can gather in these meeting notes from last year there is still no consensus on the exact syntax, and some people are still discussing the scope of the proposal.

Short version is, function call ?.() and bracket access ?.[n] parts are not loved because the syntax. The three operators ?., ?.(, and ?.[ don't do the exact same thing, so it leads to confusion.

I'd say the fact that there is an active discussion about this feature in the TC39 meetings indicate that this feature is moving up - but to be honest I could be wrong. I'm only slightly familiar with the process.

Collapse
buinauskas profile image
Evaldas

Didn't know this exists in c#! Neat

Collapse
kjellski profile image
Kjell Otto

C# is awsome, have a look here: docs.microsoft.com/en-us/dotnet/cs...
There is also async await for years now... ;)

Collapse
sammyisa profile image
Sammy Israwi Author

C# is a fantastic language :D

Collapse
buinauskas profile image
Evaldas

I'm a beginner in C# and recently I've had to make null check nested fields and now knowing chaining operators, it's much easier and simplifies my code :)

Collapse
caubeen profile image
caubeen

Wow this is awesome. I've wanted this forever. The next thing I would want is this for arbitrary conditionals such as findIndex. If findIndex returns -1 consider it errored.

Collapse
ben profile image
Ben Halpern

Wooo fun!

Collapse
blunket profile image
Andrew Siegman

This seems like a useless way to silence errors to me. (specimen?.arms?.length > 2) will return false if specimen is undefined, if arms is undefined, if arms happens to be a boolean for some reason, or if specimen.arms.length is 1.. wouldn't you need to treat these things all differently? So what's the point of this?

Collapse
sammyisa profile image
Sammy Israwi Author

It all depends on the situation. Sure, sometimes you need to check if you have specimen or arms because you would have to handle those scenarios differently. Say, notifying the user that they need to have a specimen in the first place, or populate it yourself.

But if you have specimen and all you need to do is to check if it is an alien or not, this syntax fits like a glove. I don't need to check and handle every single property before the one that I actually need, that's why in JavaScript we have a bunch of if conditions that look like this:

if(user && user.details && user.details.dateOfBirth && user.details.dateOfBirth.month === Month.March)
  console.log("You were born in March");

And as far as arms being a Boolean, this proposal is not trying to fix type issues with the language. For that kind of problems, it would be better to look at TypeScript, or Flow.

Collapse
blunket profile image
Andrew Siegman

I suppose you're right, it is inconvenient to have to do that. But I feel like this problem can be solved with a better design in the object structure. For this specific example, why would the object not just have a static boolean property like specimen.alien or something? Following a better design guideline like the Law of Demeter would solve this, right? It seems like this proposal may encourage (or at least fail to discourage) messy object structures. I only brought up the boolean example cause this is solving a common TypeError, but I see your point.

(p.s.: i am debating with good intentions, mainly cause i see no counterarguments aside from my own, not trying to start a flamewar)

Thread Thread
sammyisa profile image
Sammy Israwi Author • Edited on

Well, I do agree that the problem can be solved with better object design from the developer's side. A snippet like this one:

if(user.isBornInMarch)
  console.log("You were born in March!");

is fairly readable.

However, it depends heavily on a developer having the foresight to design a class like so and the other developers knowing of this design detail.

The other thing to remember is that this proposal is not intended to give JS a new language feature or give developers the ability to do something that they haven't been able to do before, it is just to make the life of the developer a little bit easier and the resulting code a little bit easier to read.

I understand debating with good intentions, I appreciate that. No one here wants to start an actual flamewar :)

Collapse
pomax profile image
Mike Kamermans • Edited on

How fixed is this syntax? Because while it's in draft and there is still a possibility of changing it: this is quite painful syntax, a keyword that turns an entire statement into a "is allowed to fail due to 'X does not exist' errors" would be so much nicer to work with. For instance, ... if exists [else ...] would allow for pleasant to read, easy to type expressions such as let value = ... if exist and if (... if exists else Number.MAX_VALUE > 2). This would be so much better, and would still be guaranteed to not conflict with existing code (since if cannot be followed by anything except ( right now), and can be trivially dealt with by transpilers for targeting legacy systems.

Collapse
sammyisa profile image
Sammy Israwi Author

While the exact syntax is not written in stone yet (in fact, it's subject of much debate) I'd say it's not fixed at all. However, what you're describing seems to be a different feature that fixes a different problem.

They may have some overlap in the sense that they try to change how we act on null or undefined references, but they both approach it in very different ways.

Collapse
metapgmr profile image
Jean-Jacques Dubray

You can achieve a similar result with simple functions like:

const O = (val, value = {}) => val && (typeof val === 'object') ? val : value
const A = (val, value = []) => val && Array.isArray(val) ? val : value
const S = (val, value = '') => val && (typeof val === 'string') ? val : value
const N = (val, value = 0) => isNaN(val) ? value : val
const NZ = (val, value = 1) => val === 0 || isNaN(val) ? value === 0 ? 1 : value : val
const F = (f, f0 = () => null) => f ? f : f0

const a = {}

console.log(O(a.b))             // {}
console.log(A(O(a.b).c))        // []
console.log(NZ(a))              // 1 (non-zero number)
console.log(N(a))               // 0 (number)
console.log(S(a, 'hello'))      // hello
console.log(F(a.b)(123))        // null

It's not much more overhead and it works today.

Collapse
pat_metzdorf profile image
Patrick Metzdorf

The syntax really needs to change, though.

Something like

foo.?bar?[baz] or foo.bar?[baz]? (i.e. question mark right before or after the property that may be undefined)

would be much more intuitive imho.

Collapse
briankephart profile image
Brian Kephart

Nice. I've just started using similar methods in Ruby to make my code cleaner, so it will be nice to use this pattern in JS too.

Collapse
nikolay profile image
Nikolay Kolev • Edited on

Yuck! I can't stand ?.() and ?.[]. Although I understand that the operator is ?., they should've made the ?() and ?[] sugars work, too!

Collapse
qm3ster profile image
Mihail Malo • Edited on
const a = b ? c?[0] : d?(1) : "small potatoes"

What do you see?

  • If b then safe access to c[0], else if d then 1 else "small potatoes" or
  • If b then if c an array [0] else safe call to d(1), else "small potatoes"?

Me too.

Collapse
kylessg profile image
Kyle Johnson

This has got to be the biggest syntactical pain point in JS - would love to see this

Collapse
villesau profile image
Ville Saukkonen

Now there is a simple tool to migrate your existing codebase to use optional chaining: github.com/villesau/optional-chain... :)

Collapse
sammyisa profile image
Sammy Israwi Author

Oh very interesting! Thank you for developing it!

Collapse
qm3ster profile image
Mihail Malo

I think the syntax is excellent. I hope they don't change it.

Collapse
mladenstojanovic profile image
Mladen Stojanovic

Wow! Can't wait for this one!

Collapse
egoist profile image
EGOIST

Here's also an online playground that supports this feature 馃槃

codepan.net/gist/0e78b867ead5472db...

Collapse
inf3rno profile image
J谩nszky L谩szl贸 Lajos

Great work on worsening readability of js code day by day. :S

Collapse
sammyisa profile image
Sammy Israwi Author

Why do you think so? I agree that the syntax right now is not great, and that it needs some tweaking. But overall it seems like a nice Quality Of Life improvement on the language.

Collapse
inf3rno profile image
J谩nszky L谩szl贸 Lajos • Edited on

You might be right. I just watch it from a different perspective than you. I would write if(specimen instanceof Alien) where you write if(specimen && specimen.arms && specimen.arms.length > 2). But I can accept that many devs are working with structured code, so this new feature will be fine for them. I might use it rarely too by processing nested config objects.

Collapse
anthonybrown profile image
Tony Brown

Me likey

Collapse
irvandoval profile image
Irvin Sandoval • Edited on

It will be an amazing and very useful feature 馃