Ins and Outs of the Null(ish) Coalescing Operator

Prefer video? See my Egghead lesson on Null Coalescing

The recent TypeScript 3.7 release introduced many people to some interesting Stage 3 JavaScript features (update: now at Stage 4!), including the nullish coalescing operator ??. Of course, you don’t need TypeScript to use it — you can either configure Babel to enable this feature, or just wait until it lands in the ECMA standard.

Let’s talk about why you’d want to use it, rather than the usual double pipe operator. Consider this example:

The boolean problem

const chickenPref = false;
const globalPref = true;
const serveUserChicken = chickenPref || globalPref; // evaluates to true

This user has been crystal clear that she does not like chicken — but the OR operator (||) doesn’t care. It will still continue and evaluate the globalPref, as if the chickenPref has not been defined. Why? Because the OR operator doesn’t care what exactly the value of the left operand is —it coerces the left operand into a boolean, and if it’s false, it’ll continue to evaluate the right one, resulting in one unhappy user.

Any non-true value will cause the same issue — including an empty string '', 0, NaN, and false. We’d encounter the same problem when we assign an empty string, such as a case where a user intentionally leaves a form field blank rather than answering a question, or when we intentionally assign 0 to a numeric property.

The solution: Hunting for null(ish)

Sometimes this behaviour is what we want, and we’re confident that all non-truthy values should cause the right operand to be evaluated. But if not, the solution is to be explicit and tell the code to only set the default when the value is either null or undefined (a.k.a. “nullish”):*

const serveUserChicken = chickenPref == null ? globalPref : chickenPref;

Or we can use the classic default-setting syntax with the nullish coercion operator:

const serveUserChicken = chickenPref ?? globalPref;

This will evaluate to false since chickenPref is false — without continuing onwards. Our statement is now more explicit.

More fancy tricks…

Here are a few more things you should know about using the ?? nullish coercion operator:

  1. You can combine it with the power of optional chaining:
const userPref = user.likes?.darkMode ?? true;

Optional chaining makes it safe to traverse this nested object and see whether the user has defined a darkMode preference — if not, we’ll get undefined rather than an error. And then if we would use the || operator here, we’d risk evaluating true even when the user really did not want dark mode.

2. It can be used in nested destructuring:

const { likes: { darkMode = globalPref ?? false } = user;

3. BUT — you can’t chain it with the OR operator (unless you use parentheses):

const likesChicken = chickenPref ?? globalPref || true; // BADconst likesChicken = (meatPref || chickenPref) ?? globalPref; // Good

*This could be even more explicit, but instead we’ve relied on the rules of coercion to count on undefined being coerced into null (or vice versa) when using the double equals ==.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store