Use Optional Chaining (?.)

The optional chaining operator returns the value of an object property when the object is available and undefined otherwise. .? is similar to the standard . chaining operator, with an added check if the object is defined.

The optional chaining operator enables you to write concise and safe chains of connected objects when some of those objects can be null or undefined. Before the introduction of optional chaining in ES2020, the && operator was often used to check if an object is available (obj && obj.value).

This refactoring simplifies existing checks to use the optional chaining pattern:

  • x && x.a becomes x?.a
  • x != null && x.a becomes x?.a
  • x !== null && x !== undefined && x.a becomes x?.a
  • x && x.a && x.a.b && x.a.b.c && x.a.b.c.d becomes x?.a?.b?.c?.d
  • etc.

Learn More: Optional Chaining (MDN)

The refactoring replaces falsy checks with nullish checks.

For example, when a && a.b is replaced with a?.b, it changes the execution for certain types, e.g. the empty string "" is falsy but not nullish.

However, in many cases these semantic changes will lead actually to more correct behavior. For example, text && text.length will return the empty string, but not its length, whereas text?.length will return 0 for the empty string.

Learn more: Truthy (MDN), Falsy (MDN), Nullish (MDN)

This refactoring can affects the number of calls made to methods with side effects.

For example, the refactoring changes:

let a = x() != null && x().a;

into

let a = x()?.a;

If f(1) has a side effect, it would have been called one or two times before the refactoring, and once after the refactoring. This means that the side effect would have been called a different number of times, potentially changing the behavior.

Original Code

Changes

1let value1 = x && x.a;1let value1 = x?.a;
2let value2 = x != null && x.a;2let value2 = x?.a;
3 3
4if (x !== null && x !== undefined && x.a) {4if (x?.a) {
5 console.log(x.a);5 console.log(x.a);
6}6}
7 7
8function f(obj) {8function f(obj) {
9 return obj && obj.a && obj.a.b && obj.a.b.c && obj.a.b.c.d;9 return obj?.a?.b?.c?.d;
10}10}
1let value1 = x && x.a;
2let value2 = x != null && x.a;
1let value1 = x?.a;
2let value2 = x?.a;
33
4if (x !== null && x !== undefined && x.a) {
4if (x?.a) {
55 console.log(x.a);
66}
77
88function f(obj) {
9 return obj && obj.a && obj.a.b && obj.a.b.c && obj.a.b.c.d;
9 return obj?.a?.b?.c?.d;
1010}

Transformed Code

let value1 = x?.a;
let value2 = x?.a;

if (x?.a) {
    console.log(x.a);
}

function f(obj) {
    return obj?.a?.b?.c?.d;
}

Explore More