Nullish coalescing operator - explained
Thomas Scharke
Posted on March 29, 2021
The Nullish coalescing operator is a new and additional JavaScript operator that has been available since June 2020 with ECMAScript 2020 (ES2020) of the programming language.
In addition to the well-known binary logical operators &&
(AND) and ||
(OR), it is the third operator non-binary and has the notation ??
.
It's always used when I want to explicitly check whether the value of a variable is available to use or, if the value is not available, to continue working with another value.
Here is the "classic" for me: Once with an if
block, then in a "simplified" notation with the OR operator and last but not least in the notation with the new Nullish coalescing operator.
// Long version
let secondValue = "DEFAULT_VALUE";
if (firstValue !== null && firstValue !== undefined && firstValue !== "") {
secondValue = firstValue;
}
// Shorthand with OR-Operator
secondValue = firstValue || "DEFAULT_VALUE";
// With Nullish-Operator
secondValue = firstValue ?? "DEFAULT_VALUE";
The first simplification, with the OR operator, works in most cases, but does not cover the case of working with boolean values.
But let's go through it step by step and see why the variants with the OR-operator work and then switch to the usually "better" Nullish coalescing operator.
OR-Operator
The binary logical operator (Binary Logical Operator) ||
(OR) is defined as follows:
{expression left side} || {expression right side}
I.e. if the expression on the left side delivers the value false
the expression on the right side is interpreted, otherwise the expression on the left side is interpreted.
For our "simplification" from above...
let secondValue = firstValue || "DEFAULT_VALUE";
It means that if the variable firstValue
returns the value true
, this value is returned (and in this case assigned to the variable secondValue
). However, if the variable firstValue
returns false
, the value of the right side is assigned to the variable secondValue
- in my case the value DEFAULT_VALUE
.
Step by step
Let's go through my example above step by step and see what I mean by...
The first simplification, with the OR operator, works in most cases, but does not cover the case of working with boolean values.
and how the Nullish coalescing operator helps us here.
To do this, I put my example into a function and then execute it:
function doSomethingAmazing(firstValue) {
let secondValue = "DEFAULT_VALUE";
if (firstValue !== null && firstValue !== undefined && firstValue !== "") {
// Do somthing greate
secondValue = firstValue;
}
return secondValue;
}
doSomethingAmazing(1); // 1 ✅
doSomethingAmazing(42); // 42 ✅
doSomethingAmazing(null); // DEFAULT_VALUE ✅
doSomethingAmazing(""); // DEFAULT_VALUE ✅
doSomethingAmazing(/* No value means `undefined` as value */);
// DEFAULT_VALUE ✅
doSomethingAmazing(true); // true ✅
doSomethingAmazing(false); // false ✅
🥳 Everything works fine and the code also works with boolean values. 🥳
Reflexively, I feel like "simplifying" this code and using the possibilities of JavaScript for myself. Because I can determine that a value exists with an if (firstValue)
, which leads to this version of my code:
function doSomethingAmazing(firstValue) {
let secondValue = "DEFAULT_VALUE";
if (firstValue) {
secondValue = firstValue;
}
return secondValue;
}
doSomethingAmazing(1); // 1 ✅
doSomethingAmazing(42); // 42 ✅
doSomethingAmazing(null); // DEFAULT_VALUE ✅
doSomethingAmazing(""); // DEFAULT_VALUE ✅
doSomethingAmazing(/* No value means `undefined` as value */);
// DEFAULT_VALUE ✅
doSomethingAmazing(true); // true ✅
doSomethingAmazing(false); // DEFAULT_VALUE ❌ 😮
😮 Oops...When I pass a false
to the function I get back the value DEFAULT_VALUE
and not the value false
as expected 🤔
I go one step further and "simplify" my code again; and this time I use the OR operator:
function doSomethingAmazing(firstValue) {
// Executes the right operand ("DEFAULT_VALUE")
// only if the left operand (firstValue) is falsy
// This one-liner is also called short-circuiting operator 😃
let secondValue = firstValue || "DEFAULT_VALUE";
return secondValue;
}
doSomethingAmazing(1); // 1 ✅
doSomethingAmazing(42); // 42 ✅
doSomethingAmazing(null); // DEFAULT_VALUE ✅
doSomethingAmazing(""); // DEFAULT_VALUE ✅
doSomethingAmazing(/* No value means `undefined` as value */);
// DEFAULT_VALUE ✅
doSomethingAmazing(true); // true ✅
doSomethingAmazing(false); // DEFAULT_VALUE ❌ 😮
I find the last "simplification" of my code even better. It takes away the if
block and makes the code easier to read.
But both "simplifications" lead to the same unexpected result when I call the function with the value false
.
What have I broken? 🤔
I haven't broken anything. I merely used, in both simplifications, the functionality of JavaScript that assumes that a value must be false (false
) - that is, falsy. In the concrete case, with my if
block and the OR operator, I check whether the value firstValue
is false and then use the value DEFAULT_VALUE
.
When is a value "falsy"
In JavaScript, a value is (false
) or falsy if it is null
, undefined
, 0
or false
.
And since this is the way it is in JavaScript, I have also changed the behaviour of my implementation with my "simplification" of the code 🤷.
Call the last two code examples with 0
(Zero):
doSomethingAmazing(0);
Again, I want the value 0
(Zero) to be returned, but I get - logically - the value DEFAULT_VALUE
🤷
Let's get back to the actual implementation with the following expression in the if
block:
firstValue !== null && firstValue !== undefined && firstValue !== "")
From this derives my requirement that I want to check whether a value is nullish and not whether a value is falsy, as I have (unwittingly) done through my "simplifications".
What does nullish mean
With nullish it's meant that an expression must have the values null
or undefined
, only then it is nullish.
And exactly this is and was what I wanted to have with my first implementation and have implemented.
Can I not now "simplify" my introductory example? Do I have to manually query all nullish values in JavaScript myself?
😱😱😱 N O O O O 😱😱😱
The new one - Nullish coalescing operator (??
)
This is where the new one comes into play - the third logical operator in JavaScript.
Ladies and gentlemen the Nullish coalescing operator 🚀🚀🚀, which is written in JavaScript as ??
and is defined as follows:
{expression left side} ?? {expression right side}
This operator behaves similarly to the OR operator, but with the crucial difference...
It checks if the expression on the left side is "nullish".
And not, as with the OR operator, whether the expression is false
.
A few examples of the Nullish coalescing operator:
1 ?? "DEFAULT VALUE"; // Result is: 1 ✅
42 ?? "DEFAULT VALUE"; // Result is: 42 ✅
null ?? "DEFAULT VALUE"; // Result is: DEFAULT VALUE ✅
undefined ?? "DEFAULT VALUE"; // Result is: DEFAULT VALUE ✅
true ?? "DEFAULT VALUE"; // Result is: true ✅
false ?? "DEFAULT VALUE"; // Result is: false ✅
0 ?? "DEFAULT VALUE"; // Result is: 0 ✅
"" ?? "DEFAULT VALUE"; // Result is: "" ❓
And with this knowledge, I can also "simplify" my code example again - like this...
function doSomethingAmazing(firstValue) {
// Executes the right operand ("DEFAULT_VALUE")
// only if the left operand (firstValue) is nullish
let secondValue = firstValue ?? "DEFAULT_VALUE";
return secondValue;
}
doSomethingAmazing(1); // 1 ✅
doSomethingAmazing(42); // 42 ✅
doSomethingAmazing(null); // DEFAULT_VALUE ✅
doSomethingAmazing(/* No value means `undefined` as value */);
// DEFAULT_VALUE ✅
doSomethingAmazing(true); // true ✅
doSomethingAmazing(false); // false ✅
doSomethingAmazing(""); // "" ❓
I have one more...
In my examples with the Nullish coalescing operator you will have noticed that calling my "simplified" functions with an empty string (""
) does not result in DEFAULT_VALUE
being returned.
This is not relevant to the way my example works, but I don't want to hide from you why this happens.
The answer is obvious: The nullish coalescing operator (??
) checks whether a value is nullish, i.e. whether it's null
or undefined
. And an empty string (""
) is an empty string in JavaScript and thus neither null
nor undefined
- but falsy 🤣
Another example
Let's go one step further and work with boolean values like true
and false
this time. Let's say, in the context of a configuration that should give a sign of life exactly when we are online and assumes that we are (always) online (by default):
function doSomethingAmazingWithAConfiguration({ online }) {
// We use the OR operator
let sendKeepAlive = online || true;
return sendKeepAlive;
}
// We say explicit that we're online
doSomethingAmazingWithAConfiguration({ online: true }); // true ✅
// We use the default-state
doSomethingAmazingWithAConfiguration({}); // true ✅
// We say explicit that we're offline ⚠️
doSomethingAmazingWithAConfiguration({ online: false }); // true ❌ 😮
At this point in the text I have now reckoned with the false
return value of the last call to the function, but it is not what I wanted.
I want the return value of the function to give me false
when we're offline, i.e. when we set the key online
in the passed object to false
({ online: false }
).
The known problem
From what I've learned, this wrong result of my function call makes sense. Because online || true
has the following values with the last call: false || true
.
And if the left side of the OR operator returns false
the value of the expression on the right side is used (the value of the left side is falsy) - in our case true
🤷.
The code works exactly as written, but not as expected.
Possible solutions
For my function that expects a configuration object, I could work with Destructuring and define a default value:
function doSomethingAmazingWithAConfiguration({ online } = { online: false }) {
return online;
}
Or, instead of a configuration object, I use a boolean
and check it with the strict inequality operator (!==
):
function doSomethingAmazingWithAConfiguration({ online }) {
let sendKeepAlive = online !== false;
return sendKeepAlive;
}
But in this article the Nullish coalescing operator is the star 🤩 and for my configuration function also a solution:
function doSomethingAmazingWithAConfiguration({ online }) {
// We use the Nullish coalescing operator
let sendKeepAlive = online ?? true;
return sendKeepAlive;
}
// We say explicit that we're online
doSomethingAmazingWithAConfiguration({ online: true }); // true ✅
// We use the default-state
doSomethingAmazingWithAConfiguration({}); // true ✅
// We say explicit that we're offline
doSomethingAmazingWithAConfiguration({ online: false }); // false ✅
Note
- The first version of this article I wrote in my native language because there is a very active German JavaScript Community of which I am a part and which I would like to give something back to 🙇
- Or to say it with the hashtag of my trainer buddy WebDave: #CommunityRocks and in this case #GermanJavaScriptCommunityRocksToo 😉🚀😎
Posted on March 29, 2021
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.