Nullish coalescing operator - explained
Tags: javascript, operator, beginners
Published: 2021-03-29 12:40:08

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](#or-operator--or) and last but not least in the notation with the new **Nullish coalescing operator**.

```ts
// 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](#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](#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...

```ts
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](#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:

```ts
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:

```ts
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](#or-operator):

```ts
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](#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):

```ts
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:

```ts
firstValue !== null && firstValue !== undefined && firstValue !== "")
```

From this derives my requirement that I want to check whether a value is [**nullish**](#what-means-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](#or-operator), but with the crucial difference...

> **It checks if the expression on the left side is ["nullish"](#what-means-nullish).**

And not, as with the [OR operator](#or-operator), whether the expression is `false`.

A few _examples_ of the **Nullish coalescing operator**:

```js
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...

```ts
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](#which-means-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_](#wann-is-a-value-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):

```ts
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](#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](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment) and define a default value:

```ts
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_ (`!==`):

```ts
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:

```ts
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](https://bit.ly/tscharke-nullish-coalescing-operator) I wrote in my native language because there is a very active [German JavaScript Community](https://discord.gg/7U3U4epQmc) 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](https://twitter.com/webdave_de): [#CommunityRocks](https://twitter.com/hashtag/communityrocks) and in this case [#GermanJavaScriptCommunityRocksToo](https://twitter.com/hashtag/GermanJavaScriptCommunityRocksToo) 😉🚀😎