JS checking deep object property existence [duplicate]

I'm trying to find an elegant way to check the if certain deep properties exist in an object. So practically trying to avoid monstrous protective checks for undefined eg.

if ((typeof error !== 'undefined') && 
  (typeof error.responseJSON !== 'undefined') &&
  (typeof error.responseJSON.error) && 
  (typeof error.responseJSON.error.message)) {
      errorMessage = error.responseJSON.error.message;
}

What I'm thinking about is a convenience-function like

if (exists(error.responseJSON.error.message)) { ... }

Any ideas? For convenience, the use of underscore-library is ok for the solution.


There are several possiblities:

Try-catch

try {
  errorMessage = error.responseJSON.error.message;
} catch(e) { /* ignore the error */}

Fails for:

Object.defineProperty(error, 'responseJSON', {
  get: function() { throw new Error('This will not be shown')
});

&&

errorMessage = error && error.responseJSON && error.responseJSON.error && error.responseJSON.error.message;

Fails for:

error.responseJSON = 0;
// errorMessage === 0 instead of undefined

function

function getDeepProperty(obj,propstr) {
  var prop = propstr.split('.');
  for (var i=0; i<prop.length; i++) {
    if (typeof obj === 'object')
      obj = obj[prop[i]];
  }
  return obj;
}

errorMessage = getDeepProperty(error, 'responseJSON.error.message');

// you could put it all in a string, if the object is defined in the window scope

Fails for:

// It's hard(er) to use

function alternative - see comment by @Olical

function getDeepProperty(obj) {
  for (var i=1; i<arguments.length; i++) {
    if (typeof obj === 'object')
      obj = obj[arguments[i]];
  }
  return obj;
}

errorMessage = getDeepProperty(error, 'responseJSON', 'error', 'message');

Try this underscore mixin to look up a variable with a path. It takes an object and string and t

_.mixin({
    lookup: function (obj, key) {
        var type = typeof key;
        if (type == 'string' || type == "number") 
            key = ("" + key).replace(/\[(.*?)\]/, function (m, key) { //handle case where [1] may occur
                return '.' + key.replace(/["']/g, ""); //strip quotes
            }).split('.');
        for (var i = 0, l = key.length; i < l; i++) {
            if (_.has(obj, key[i])) 
                obj = obj[key[i]];
            else 
                return undefined;
            }
        return obj;
    }
});

Now call in your example:

_.lookup(error, 'responseJSON.error.message') // returns responseJSON.error.message if it exists otherwise `undefined`