The nullish operator 🔗
The nullish operator takes the form of a double question mark,
??, and is used in any expression to return the left or right side of the operator. The left hand is returned unless it is null or undefined (hence "nullish"), in which case the right side is returned.
In the above example,
foo is assigned the value "something" because the left-hand side of the operator evaluates to undefined. Let's see a few more examples:
let thisIsNull = null;
let foo2 = thisIsNull ?? 'something'; //something
let foo3 = 5 ?? 'something'; //5
foo2 is assigned the value "something" because
thisIsNull evaluates to
null. But in the final example, because the left side of the operator is not nullish - it is 5 -
foo3 is assigned the value 5, and the right side of the operator, "something", is ignored.
Differences vs. the logical OR operator 🔗
||), you might think this looks rather familiar. Indeed, all three of the examples above would work exactly the same if we swapped the nullish operator for the OR operator.
The difference, however, is that the OR operator returns the left side unless it is falsy. The nullish operator is more strict; it returns the left side unless it is nullish (null or undefined).
Let's try an example where the two operators would yield different results:
With the first example, the result is the right side - 5 - since we're using the OR operator and the left side is falsy. With the second example, the result is the left side - 0 - since, while it is falsy, we're using the nullish operator and that is concerned only with nullish values, not falsy ones.
The optional chaining operator 🔗
Next up is the optional chaining operator,
?.. This is designed to reduce the amount of code needed to check the existence or value of a nested property in an object.
Consider the following:
That's a bit laborious, just to find out if the property we're ultimately interested in,
obj.foo.bar.final, exists or not.
obj.foo, even though it doesn't exist, but if we try to go to two levels, and the first doesn't exist, we'll get an error:
This is why we have to interrogate each level at a time. Using the AND (
obj.foo doesn't exist, so the other checks (for
obj.foo.bar.final) never run.
Anyway, so much for the back story. The optional chaining operator alleviates the need for all of this hoop-jumping. We can rewrite the above with:
Suddenly there's no need to worry about errors or to check each level step by step. The
Use with dynamic properties 🔗
OK that's great for dot syntax, where we know the names of our properties. But what about with dynamic properties, using square-bracket syntax?
That will error. Perhaps counter-intuitively, to use the optional chaining operator with square-bracket syntax we actually need the dot as well. So the corrected version would be:
Weird having a dot before a square bracket, no? But the thing to remember is that, when using dot syntax, the dot doubles as both the generational separator and the dot belonging to the operator. With square-bracket syntax, it acts only as part of the operator. In other words, the operator is always
?, even when using square-bracket syntax.
Use with methods 🔗
If you're really lazy, you can even use the optional chaining operator to call methods which may or may not exist:
Or, if the method lies at a deeper level:
I hope you found this helpful and enjoy using these operators in your code!
Did I help you? Feel free to be amazing and buy me a coffee on Ko-fi!