Nullish Coalescing and Optional Chaining in JavaScript

When working with data, situations arise where you aren’t sure that the property you need exists. To build fault-tolerant applications despite issues like this, default values are provided to make apps fail gracefully. Before now, the logical OR (||) operator is used to achieve this. It is a syntax for selecting the second defined value or variable (default value) if the first value or variable is falsy (false, 0, ' ', null or undefined).

The logical OR (||) operator

The tenary condition a ? a : b is shortened to a || b. This concept is known as short-circuiting.

The result of actualValue || defaultValue is: actualValue if actualValue is truthy, otherwise, it is defaultValue.
console.info(null || 'default');       //'default'
console.info(undefined || 'default'); //'default'
console.info(false || 'default');    //'default'
console.info(' ' || 'default');    //'default'
console.info(0 || 'default');     //'default'

Note that the default value is also used when the actual value is false, 0 or ' '. This is due to JavaScript evaluating these values as falsy. The operand on the left hand side was coerced to a boolean for the evaluation and any falsy value (false, 0, ' ', null or undefined) is not returned. This leads to unexpected results if these values are considered valid.

To get a better grasp, let's consider the function:

function getUser(user) {
  return user.name || 'guestUser';
}

function isUserValid(user) {
  return user.isValid || true;
}

const users = [
  {name: 'Ethel', age: 25, isValid: true},
  {name: '', age: 22},
  {age: 18, isValid: false}
];

users.map(user => getUser(user));
// Expected output: ["Ethel", "guestUser", "guestUser"];

users.map(user => isUserValid(user));
// Expected output: [true, true, true];

If the expected value for the second object in the array above is ' ', the logical OR (||) operator nullifies it. A recent operator (??) has been added to JavaScript to address unexpected behaviours like this when using the || operator.

The nullish coalescing (??) operator

As opposed to the logical OR (||) operator, the nullish coalescing (??) operator provides a syntax for selecting the second operand only when the first operand evaluates to either null or undefined (but no other falsy values).

The nullish coalescing operator (??) is a logical operator that returns its right-hand side operand when its left-hand side operand is null or undefined, and otherwise returns its left-hand side operand.

The tenary condition (a !== null && a !== undefined) ? a : b is shortened to a ?? b.

The result of actualValue ?? defaultValue is: actualValue if actualValue is not null or undefined, otherwise, it is defaultValue.
console.info(null ?? 'default');      // 'default'
console.info(undefined ?? 'default'); // 'default'
console.info(false ?? 'default');   // false
console.info(' ' ?? 'default');    // ' '
console.info(0 ?? 'default');     // 0

Take a look at the last three operations, therein lies the difference between || and ??. Actual values are returned for the two falsy values.

Rewriting the same function,

function getUser(user) {
  return user.name ?? 'guestUser';
}

function isUserValid(user) {
  return user.isValid ?? true;
}

const users = [
  {name: 'Ethel', age: 25, isValid: true},
  {name: '', age: 22},
  {age: 18, isValid: false}
];

users && users.length > 0 &&
  users.map(user => getUser(user));
// Expected output: ["Ethel", "", "guestUser"];

users && users.length > 0 &&
  users.map(user => isUserValid(user));
// Expected output: [true, true, false];

Optional Chaining (?.)

The optional chaining operator (?.) provides a concise way of accessing the value of a property that is deep within a chain of connected objects without having to explicitly validate the reference. It is useful when accessing a property of an object which may be nullish (null or undefined).

Instead of causing an error when a reference is nullish, it short-circuits with a return value of undefined.

Given the object,

const user = {
  name: 'Eve',
  state: {
    name: 'Lagos',
    city: {
      name: 'Gbagada',
      code: 234
    }
  }
};

To access a property on this object that its availability is uncertain, we usually use the syntax below:

if(user.state && user.state.city) {
  const stateCode = user.state.city.code;
}

This ensures that the program does not throw an error message such as:

TypeError: Cannot read property 'code' of undefined

With the newly introduced optional chaining operator (?.), you can check for data availability using a more concise approach.

console.info(user.state?.city?.code);

Given the array,

const users = [
  {
    name: 'Nora',
    state: {
      city: {
        name: 'Accra',
        code: '233',
      },
    },
  },
  {
    name: 'Terrence'
  },
  {
    name: 'Nick',
    state: {

    },
  },
];

const cityCode = users?.length > 0 &&
  users.map(user => user.state?.city?.code ?? 0);
// Expected output: ["233", 0, 0];
Do not combine both the AND (&&) and OR operators (||) directly with ??. A syntax error would be thrown in such scenario.

Note that when ?? is used together with || or && operator in an expression, always enclose the expression within parentheses due to its low value on the precedence table.

Given the object,

let user = {
  name: 'Eve',
  state: {
    name:  null
  }
};

const stateValueWithError = user.state &&
  user.state.name ?? 'Accra';
  // Uncaught SyntaxError: Unexpected token '??'

console.info(stateValueWithError);
// Uncaught ReferenceError: stateValueWithError is not defined

const stateValueWithoutError = (user.state &&
  user.state.name) ?? 'Lagos';

console.info(stateValueWithoutError); // "Lagos"

Conclusion

The main difference between the logical OR (||) and the nullish coalescing operator (??) is that || returns the first truthy value while ?? returns the first defined value. This becomes important when other falsy values are considered as valid values.

Though this feature is appealing to use, minimize its usage at this stage due to the cross-browser stability.

Article Tag  JavaScript

Share this article:

Stay Updated

    More Articles


    I write about accessibility, performance, JavaScript and workflow tooling. If my articles have helped or inspired you in your development journey, or you want me to write more, consider supporting me.