JmJ
JmJ

Reputation: 2098

Using a try catch around an object literal

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?

Upvotes: 6

Views: 1328

Answers (2)

chriskelly
chriskelly

Reputation: 7736

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).

Upvotes: 2

crackpotHouseplant
crackpotHouseplant

Reputation: 401

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.

Upvotes: 0

Related Questions