Accessing deeply nested data

May 6, 2020 — 3 min read

Getting an Uncaught TypeError: Cannot read property error in JavaScript has happened to me too many times. The error has even been ranked the most frequent JavaScript error. Especially when working with external data sources this can be problem. Consider the following example from the harmonie project, which parses agricultural field data from various input files (mostly XML):

let applicationYear
// try to access the application year from the farm subsidy form
try {
  applicationYear = data['fsv:FSV']['fsv:FSVHeader']['commons:Antragsjahr']
} catch (e) {
  // no application year found -> go on as it is optional anyway
}

The try...catch block will now capture the error thrown when a property cannot be read. We could see this as ‘defensive code’, as we can’t trust the input data and have to consider missing properties in the nested tree. Instead of repeating the try..catch block for every variable I have been using the following function in the past (e.g. in the ELAN-Paser):

function getSafe(func) {
  try {
    return func()
  } catch (e) {
    return ''
  }
}

const applicationYear = getSafe(() => data['fsv:FSV']['fsv:FSVHeader']['commons:Antragsjahr'])
// applicationYear is either the correct applicationYear from the data object OR undefined

This shortens the amount of code to write every time you are unsure whether a property exists in an object or not. While it is not much extra code, it is still repetitive and the function needs to be imported/defined in every new project. With the JavaScript standard ES2020, we don’t need this anymore.

A better way with optional chaining introduced with ES2020

With the optional chaining feature introduced in ES2020, we don’t need to define a getSafe function first, but can rather rely on native methods:

const applicationYear = data['fsv:FSV']?.['fsv:FSVHeader']?.['commons:Antragsjahr']
// applicationYear is either the correct applicationYear from the data object OR undefined

Awesome, isn’t it? As of today, all up to date versions of modern browsers (Chrome,Firefox,Edge,Safari,Opera) support optional chaining. If you need support for older browsers, there also is a babel plugin as well.

Uncaught TypeErrors that cannot be solved by optional chaining

Another classic example for getting the error is by misspelling a property name in a nested object:

const field = {
  properties: {
    cropCode: 415
  }
}

// misspelling key 'properties'
const cropCode = field.propetries.cropCode 
// will thrown: Uncaught TypeError...

Obviously, this cannot be solved by optional chaining, as the information is not optional. Using TypeScript would have saved me from this error many times, however as of right now I’m just too lazy to set it up and annotate all of my code properly. Maybe in the not so distant future I will give it a try…