DEV Community

Prevent Object Retrieval TypeError with &&

Samantha Ming on March 11, 2019

When you retrieve an object value that’s undefined, it will crash due to a TypeError! So make sure you do an initial check with an if statement. ...
Collapse
 
moopet profile image
Ben Sinclair

This is something you see a lot when stringing shell commands together.

mkdir my_directory && cd my_directory || exit(1)

This way of using && before running a function (or command) and calling it a "guard operator" makes it seem like it's not what it actually is (an expression).

It gets a little odd when you try to do things that seem to make sense on the face of it, like use an assignment instead of a final expression:

foo = {};

foo.bar || "hello";
// "hello"

console.log(foo.thisworks = "hello")
// "hello"

foo.bar || foo.bar = "hello";
// Invalid left-hand side in assignment

foo.bar && foo.baz = "hello";
// Invalid left-hand side in assignment

foo.bar && (foo.baz = "hello");
// "hello"

And it can be tempting to string too many of them together and make quite a confusing line.

I prefer to be explicit, and to dive out if the condition isn't met:

function do_the_foo(foo) {
  if (typeof foo.bar == "undefined") {
    // handle stuff or...
    return;
  }

  // continue...
  console.log(foo.bar.baz);
}
Collapse
 
tdubs profile image
Thom W

Maybe 'Gate' would be a better word?

Collapse
 
drbearhands profile image
DrBearhands

Why would one prefer the && operator over a conditional? At first glance, the later seems 'better' in many ways:

  • closer to truth
  • less language-dependent, especially if you use if (x.y !== undefined)
  • better control flow, as x.y && will result in undefined if x.y is undefined, rather than entering an else clause

It's a one-liner, sure, but I would argue ternary operators are a better fit: x.y ? f(y) : g() over (x.y && f(y)) || g().

Not saying you're wrong, but I'm curious about the reasoning.

Collapse
 
hjess profile image
Howard Jess

I'm in agreement with those opposing this idiom. The Javascript phrase:


obj && obj.prop && obj.prop.send();

is an expression, and (to me at least) should be pure, like a pure function. As used above, this expression is used only for its side effect. I would always prefer to be explicit. I like the proposed optional chaining; but until that's available, write this statement in English:


// pedantic
if (obj && obj.prop && typeof obj.prop.send === 'function') {
obj.prop.send();
}

Collapse
 
samanthaming profile image
Samantha Ming • Edited

That’s the really interesting thing about JS or programming in general. There are always multiple ways of solving things. Some of them are definitely a syntactic choice. I’m always in the camp of expanding my toolkit so when I run across it (especially from others code), I know what’s going on. But at the end of the day, you’re the owner of your code, so pick the way that makes the most sense to you 😊

Collapse
 
misterwhat profile image
Jonas Winzen

You could use OR defaulting to protect against Type Errors, too:

(((obj||{}).prop1||{}).prop2||{}).prop3

Or take this a step further and write a helper for this:

(iterative version)

const getObjPath = (path = []) => obj => {
  let currentVal = obj;
  for (const prop of path) {
    currentValue = (currentVal || {})[
      prop
    ];
    if (!currentValue) {
      return currentValue;
    }
  }
  return currentValue;
};

(recursive version)

const getObjPath = ([
  prop,
  ...path
] = []) => (obj = {}) =>
  path.length && obj[prop]
    ? getObjPath(path)(obj[prop])
    : obj[prop];

Collapse
 
samanthaming profile image
Samantha Ming

I typically use the “||” for setting the default values. Thanks for sharing your solution 🙂

Collapse
 
qm3ster profile image
Mihail Malo • Edited

Is it just me, or does it feel like the elvis operator will only enable us to be more negligent?
Nothing stops us writing

const d = a?.b?.c?.d
if (d) doStuff(d)

when what we really needed was

const d = a.b?.c.d
if (d) doStuff(d)

Which will make the code not "fail fast", even though there's no correct logic when the a has no b or a present c has no d.

The currently possible code:

const {c} = a.b
if (c) doStuff(c.d)

tells us a lot more about the structure. And it's not a token longer!

The main problem here is that a 3-level destructuring is seen as "showing off" most of the time, and (often correctly) shamed as "unreadable", here one can just say this is "defensive programming". Which it's not, since you are failing to throw valuable errors in the "safe" inner code.

So, I am sure there are genuine uses, I have many places in my code I'd like to use it myself, especially the ?.(). But I fear it will be overused, and its overuse "well-justified".

Collapse
 
samanthaming profile image
Samantha Ming

You’re absolutely right! It’s one of those common JS saying, just because you can doesn’t mean you should. When it’s used appropriately, it’s super helpful. And if you abuse it, it becomes a code smell. Definitely something to be careful of. Thanks for pointing that out 👍

Collapse
 
ggenya132 profile image
Eugene Vedensky

That spicy proposal looks exciting. 'Elvis' operator is totally suited to javascript

Collapse
 
samanthaming profile image
Samantha Ming

Hahaha, I think I learned something new! Never knew that was called the ‘elvis’ operator 😂

Collapse
 
ggenya132 profile image
Eugene Vedensky

I first heard it referred to as that when I was reading through a groovy scripting tutorial!

Thread Thread
 
scheidig profile image
Albrecht Scheidig • Edited

The Elvis operator ?: returns left-hand side if it is trueish, right-hand side otherwise, thus comparable to object || default. groovy-lang.org/operators.html#_el...
It is called Elvis because it looks like an Elvis emoticon, but I always memoized it with "Not sure if he still exists" :-)

  king?:sing()

OTOH, the safe-navigation operator ?. will return left-hand side if it is null, otherwise evaluate right-hand side on the result of left-hand side, thus comparable to object && object.prop groovy-lang.org/operators.html#_sa...

So the proposal for JavaScript is not the Elvis operator, but comparable to the safe-navigation operator.

Thread Thread
 
samanthaming profile image
Samantha Ming

Indeed! Yes, thanks for the correction. In my new blog post, I talk about the Elvis Operator and hopefully others won't make the same confusion as I had 😅

Collapse
 
ancientswordrage profile image
AncientSwordRage • Edited

The issue comes when people chain up 5 at once and you get bogged down with the 4 or so properties before the one you're trying to get.

Much prefer lodashes has or object destructuring.

Collapse
 
tdubs profile image
Thom W

I always felt using the && operator in this manner was an unintentional. And that its a quirk of JS. Like it should only be used in the if statement. Nevertheless it's super useful.

Collapse
 
samanthaming profile image
Samantha Ming

Definitely something to keep in your toolkit. Even if you don’t use it. If you ever come across that code, at least you will be able to follow what other people are trying to do 😊

Collapse
 
motss profile image
Rong Sen Ng