6

Inside a callback I build an object I build to send out in my Express app:

this.response = {
      owner: data.actions[1].causes[0].shortDescription,
      build_version: data.actions[0].parameters[0].value,
      branch_to_merge: data.actions[0].parameters[1].value,
      jira_tickets: data.actions[0].parameters[2].value,
      build_description: data.actions[0].parameters[3].value,
      outcome: data.result
    };

If I get a different response than what I'm used to I get a fatal cannot find 'property' of undefined error that crashes my server caused by these: data.actions[1].causes[0].shortDescription.

I was wondering what to do about it and I thought I could just put it in a try catch but that seems wrong.

What's considered a good way of dealing with this?

JmJ
  • 1,989
  • 3
  • 29
  • 51
  • [short circuiting](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Logical_Operators#Short-Circuit_Evaluation) could help, but would be rather messy with how deeply nested your properties are. – Kevin B Apr 20 '16 at 19:26
  • 1
    `if-else` statements? :) Good way is to handle different scenarios and construct proper data based on that. If you don't know what's going on, then it's not good. – Andrey Popov Apr 20 '16 at 19:28
  • 1
    Accessing nested data in this way is like defusing bomb, you never know when it explodes. I would suggest libraries that add the checking the path your accessing, O.get("data.actions.1.causes.0") https://github.com/mariocasciaro/object-path, also the lodash, ramda has methods to access nested data safely. – Risto Novik Apr 20 '16 at 20:09

2 Answers2

2

1. use try/catch

In your statement you have:

  • 26 potential sources for Reference errors (16 missing objects, 10 array out of bounds).

That's what try/catch handles best. Dealing with the same type of error from a block of code and handling in one place. So unless you want to deal with each potential error individually, try/catch is the simplest and best tool you at your disposal. It works and you can pinpoint the problem:

var w = {}
try {w.x.y.z} catch(e) {e}

TypeError: Cannot read property 'y' of undefined(…)

Alternatively, if you need more control, you could

2. test all references up-front

If it must succeed even if some properties fail, you can check each reference. e.g.:

var sdValid = data && 
            data.actions && 
            data.actions.length>1 && 
            data.actions[0].causes && 
            data.actions[0].causes.length && 
            data.actions[1].causes[0].shortDescription;
var owner = sdValid
            ?  data.actions[1].causes[0].shortDescription 
            : 'default value';
this.response = {
    owner : owner,
    // etc... 
}

You can easily make it more readable if you

3. roll out your own reference checker

this.response = {
    owner : nestget(data, 'nobody')
               .prop('actions', 1)
               .prop('causes', 0)
               .prop('shortDescription')
               .value(), 
               // return shortDescription if you got this far or 'nobody'

    build_version : nestget(data, '0.0.0')
               .prop('actions', 0)
               .prop('parameters', 0)
               .prop('value')
               .value(),
               // return value if you got this far or '0.0.0'
    // ... etc
};

// Custom - easy to use reference checker
function nestget (obj, default) {
   var valid = false;
   this.default = default;
   this.obj = obj;
   var that = this;

   return { 
       prop : propChecker,
       value : getValue
   }

   function propChecker (prop, i) {
      // implementation omitted for brevity
   }
   function getValue () {
      return value;
   }
}

4. use a library

Last but not least, you can always use a library. Personally I like how XPath searches XML trees so I would suggest using something like JSONPath to search for nested objects in a data structure. e.g.

this.response = {
    owner : jsonpath(data, '$..shortDescription[0]'),
    build_version jsonpath(data, '$.actions[0].parameters[0].value'), // no error
    // ... etc
}

However, there are lots of options out there (e.g. lodash/underscore as mentioned in a comment).

chriskelly
  • 7,526
  • 3
  • 32
  • 50
0
try {
    //unwrap your data
}
catch(err) {
    //handle the failure
}

is a good solution, depending on how nuanced you want to be

If you want to continue with execution even if one or more is failed, you might consider the conditional assignment operator

javascript: is this a conditional assignment?.

this.response = {
      owner: data.actions[1].causes[0].shortDescription || undefined,
      etc: data.something || undefined
    };

Of course, you will have to make sure the stuff that handles this.response upstream is not going to blow up for the same reason.

Edit: The above is not working for me, though I thought it was a feature of javascript. If you can't get it to work, you can accomplish the same thing with the ternary operator

var asdf = (jkl == undefined) ? 3 : jkl;

Which says

if(asdf == undefined) asdf = 3; else asdf = jkl 

The trouble with this is that if you do

var asdf = (nested.variable == undefined) ? 3 : nested.variable;

Your code will break if nested is undefined. There are a few workarounds to this, such as setting a number of flags and then conditioning your ternary on those.

I've heard (and agree) that nested ternary statements is not a very good idea.

Another edit: Check over here How to determine if variable is 'undefined' or 'null'? for issues regarding variables that may or may not be defined. Apparently == undefined and == null only work if the variables are declared but not defined. Which actually, I think that is the case for you. When in doubt though, use typeof.

Community
  • 1
  • 1
crackpotHouseplant
  • 380
  • 1
  • 3
  • 12
  • conditional assignment won't help if any part of the nested object is undefined. – chriskelly Apr 20 '16 at 19:40
  • added another bit about potentially undefined variables – crackpotHouseplant Apr 20 '16 at 20:02
  • Looking at the problem from a different angle u could normalise the nested structure into a flatter structure and then have your usual check. There a number libraries you could use just google normalise js. – Deep Apr 20 '16 at 23:02