Sometimes we deal with deeply nested objects and we need to add extra checks to prevent our app from breaking while trying to access undefined parts of that object.

Here's a real life example that I found while reviewing code:

function isFileSizeTooLargeError(error) {
  if (!error) return false;
  if (!error.networkError) return false;
  if (!error.networkError.result) return false;
  if (!error.networkError.result.error) return false;
  return error.networkError.result.error.includes('file size too large');
}

Don't worry too much about what's going on, but if you want some context this is an error checking function for a GraphQL query response made with react-apollo.

For reasons that don't matter for this post, we can't be sure that we will have every piece of the object we are checking and we only care about the text included on the last error.

If we didn't do any check and we just ran the includes check, we may get different exceptions, like:

  • TypeError: Cannot read property 'includes' of undefined
  • TypeError: Cannot read property 'error' of undefined

That's why all those checks were included.

We can simplify our code by acknowledging that there may be exceptions and that we don't care about them.

function isFileSizeTooLargeError(error) {
  let fileSizeTooLarge = false;
  try {
    fileSizeTooLarge = error.networkError.result.error.includes('file size too large');
  } catch (ignoreThisError) {
    // something went wrong, we don't care exactly why
    // the string we look for is not there
  }
  return fileSizeTooLarge;
}

Note that this implementation has more lines of code than the previous one, but there are fewer lines that actually do something that we need to stop and think about.

Any exception on this context means that the string we are looking for isn't there, we can safely ignore it (empty catch).

Have in mind that this is no silver bullet; depending on your implementation it may be better to have several conditionals or handle different kind of exceptions.

Future

This is a well known problem and there are many ways of dealing with it. One of them is to improve Javascript itself.

There's work being done to include a new syntax to JS that simplifies use cases like this.

The proposed change is called Optional Chaining, at the time of writing this it's on Stage 1.

Using that syntax our code would look like this:

function isFileSizeTooLargeError(error) {
  const fileSizeTooLarge = error?.networkError?.result?.error?.includes('file size too large');
  return Boolean(fileSizeTooLarge);
}

There's already a Babel plugin for this so you can play around with it, see https://github.com/babel/babel/pull/5813

Having said that, I don't think you should use this now in your apps :).