How to safely get deeply nested properties in ESNext

Safely get deep nested props with JavaScript’s proposed nullish coalescing and optional chaining operators.

If you’re experienced with JavaScript, you’ve probably seen this while working with deeply nested objects more times than you’re willing to admit:

Uncaught TypeError: Cannot read property 'length' of undefined

Optional chaining operator

const pageTitle = props.params?.suppliedTitle;

If props.params.suppliedTitle is null or undefined instead of throwing an error, it will return undefined.

Nullish coalescing operator

const pageTitle = suppliedTitle ?? 'Default Title';

The nullish coalescing operator looks similar to || at first, but wont short circuit if the value of suppliedTitle is false or anything which coerces to false.

The old method

There are many ways to solve this but usually involves highly verbose expressions, or third party library functions. Here are examples of the two:

// Our example object
const response = {
  posts: [
    {
      body: '...',
      comments: [
        {
          body: '...',
          showEmail: false,
        }
      ],
    },
  ];
};

// Plain JS way
const showEmail =
  response.posts &&
  response.posts.length &&
  response.posts[0].comments &&
  response.posts[0].comments.length &&
  response.posts[0].comments[0].showEmail;

// Using Ramda
const showEmail = R.path(['posts', 0, 'comments', '0', 'showEmail'], response);

As you can see it gets unwieldy quickly. Both of these will simply return undefined if the value is not accessible, instead of throwing an error.

Returning a default value

You can return a default value like so:

// Plain JS way
const showEmail =
  (response.posts &&
    response.posts.length &&
    response.posts[0].comments &&
    response.posts[0].comments.length &&
    response.posts[0].comments[0].showEmail) ||
  true; // Bug, always returns true

// Using Ramda
const showEmail = R.pathOr(
  true,
  ['posts', 0, 'comments', '0', 'showEmail'],
  response,
);

While the Ramda example is fine, the plain JS way will actually always evaluate to true because it’s value is false and forces the code to short circuit the ||.

New way

The null coalescing operator and optional chaining operator attempt to solve these problems in a succinct way.

// Return undefined if property doesn't exist
const showEmail = posts[0]?.comments?.[0]?.showEmail;

// Return default value of true if property doesn't exist
const showEmail = posts[0]?.comments?.[0]?.showEmail ?? true;

This works regardless if the values is false or not, and will only short circuit if the value is undefined or null. The code is very easy to understand what is actually doing compared to previous examples.

Read next:

Use new pipeline operator with React HOCs

Hello Blogosphere!