lookout.devlookout.dev
search
Share Knowledge
10

Use the Nullish Coalescing Operator over Ternary or Logical-or Operators

Monday, August 17, 2020

The nullish coalescing operator was introduced in the ECMAScript 2020 specification as a binary operator, which for most JavaScript developers replaces the use of the logical-or operator for setting a default/fallback value.

Logical Or

Prior to ECMAScript 2020 there were two common approaches to a binary operation with a default/fallback value, either the logical-or operator, or the ternary operator. Let's look at both of these.

First, let's look at how the logical-or operator can be used with a fallback value.

interface Props {
  score: number;
}

export default ({ score }: Props) => 
 <div className="score">{ score || '-' }</div>;

Breaking this down:

  • Our component expect a score prop, which will display the score of the game.
  • If the score value is not provided we use the logical-or operator to display a dash ( - ), likely indicating that the game has not yet started.

The benefits of this approach are:

  • A boolean test is performed on the score value, and if the boolean test results in false, then the right side of the logical-or operator is the result of the expression.
  • If the boolean test result is true, then the left side of the logical-or operator is the result of the expression.
  • The value is only evaluated once. Below we'll see how this compares to using the ternary operator.

Therefore, if the score value is "truthy", then the score is rendered, and if the value is "falsey", then the dash ( - ) is rendered.

However, there is a foot gun to this approach. If the score value is 0 (or falsey), then the boolean test results in false, and the right-hand side of the logical-or operator is the result of the expression.

Ternary Operator

A second common method for a binary operation is the ternary operator. Let's look at our same Score component using this approach:

interface Props {
  score: number;
}

export default ({ score }: Props) => 
 <div className="score">{ score ? score : '-' }</div>;

In this example we are using the ternary operator where a boolean test is performed on the score value, and if true, then the result of the expression is the evaluation of the code that is on the left side of the colon ( : ). And, if the boolean test result is false, then the result of the expression is the evaluation of the code on the right side of the colon ( : ).

The potential problem with the ternary operator approach is that the value, score in this example, is evaluated twice. First, it is evaluated as part of the boolean test, and then again, as the result of the expression if the boolean test is true. While in this particular example that may not be issue, this could be an issue if the value under assertion is a function, and further, if the value under assertion performs a side effect.

It should be noted that using the ternary operator approach we can avoid the foot gun we observed in the logical-or approach, namely, we can explicitly check the value. For example:

interface Props {
  score: number;
}

export default ({ score }: Props) => 
 <div className="score">{ (score !== null || score !== undefined) ? score : '-' }</div>;

By explicitly checking the value of score to be either null or undefined we can ensure that the score of 0 is rendered properly.

This brings us to the conclusion of the necessity for a succinct method for asserting a value to be not null or not undefined with a default/fallback value for the expression.

Nullish Coalescing

While the name in itself may be confusing, the nullish coalescing operator introduced in ECMAScript 2020 is currently widely adopted by browsers and provides for a succinct method for asserting a value is not null or undefined with a fallback value for the expression.

Let's look at an example using the nullish coalescing operator:

interface Props {
  score: number;
}

export default ({ score }: Props) => 
 <div className="score">{ score ?? score : '-' }</div>;

This looks very similar to the first approach of the logical-or operator, and also quite similar to the ternary operator.

Here, we use the nullish coalescing operator (two question marks) to output the score value as long as the value is not null or undefined. This code is equivalent to the previous example of the ternary operator where we checked for null and undefined with the exception that the value is not evaluated twice. This avoid the potential issue with the ternary operator approach of evaluating the value twice, and the potential of invoking side effects unecessarily.

Instructions

checkmark-circle
Do

use the nullish coalescing operator for effective and succinct binary operations with a fallback value

error-circle
Avoid

the logical or operator for fallback values

the ternary operator for fallback values

Code Examples

use the nullish coalescing operator for binary operations with a fallback value

let a = b ?? c; // ✅ effective and succinct check for null and undefined with a fallback value

the logical or operator for fallback values

let a = b || c;  //  ⚠️ b could be a falsey value and result erroneous in the fallback value of c

the ternary opeerator for fallback values

let a = b  ? b : c; // ⚠️ b is evaluated twice

the ternary operator for fallback values

let a = (b !== null && b !== undefined) ? b : c; // ⚠️ b is evaluated twice
Brian Love

I am a software engineer and Google Developer Expert in Web Technologies and Angular with a passion for learning, writing, speaking, teaching and mentoring. I regularly speaks at conferences and meetups around the country, and co-authored "Why Angular for the Enterprise" for O'Reilly. When not coding, I enjoy skiing, hiking, and being in the outdoors. I started lookout.dev to break down the barriers of learning in public. Learning in public fosters growth - for ourselves and others.

Google Developers Expert

Have a question or comment?

Cédric Duffournet
Cédric Duffournet ·

Hello, Thank you for this article, it's very well explained

I think there is little a mistake in your nullish coalescing operator example:

this : <div className="score">{ score ?? score : '-' }</div>;

Should be <div className="score">{ score ?? '-' }</div>;