DEV Community

Cover image for JavaScript gotchas - not all your conditions are booleans
Alois Sečkár
Alois Sečkár Subscriber

Posted on

JavaScript gotchas - not all your conditions are booleans

One thing I like about JavaScript is its shothand evaluation whether a variable contains a value. Instead of the tedious if (text !== undefined && text !== null && text.length > 0) comparison we can just write if (text) and it works the same.

You just need to be careful if you want to use more such conditions. When you type:

let text1 = 'text1'
let text2 = 'text2'
console.log(text1 && text2)
console.log(text1 || text2)
Enter fullscreen mode Exit fullscreen mode

you won't get true, as inattentive JS devs tend to expect from time to time, but 'text2' from the first and 'text1' from the second log. Huh? 😵

The truthiness of variable text1 is still being evaluated first. Therefore, the program advances to text2, but after the evaluation is finished, JavaScript is left with the real value of the string, and so it returns it. The unsuspicious machine doesn't expect you didn't feed it boolean values as you properly should. In the later case with "or", text1 is truthy, so it satisfies the condition and the actual value of the variable is dropped from the expression. The simple if (text) in fact does exactly the same. But since we don't work with the returned value any futher as we just wanted to know if something is there, we don't even notice and it works for us. But not anymore with two or more variables.

Similar shenanigans are probably the reason why many programmers gained feeling that JavaScript is all but an evil and mess that should be avoided at all costs. While it is rather their fault when they try to cram illogical combinations into it. “Play stupid games, win stupid prizes.”.

I originally thought I'd go in deeper deatail about the rules for evaluating operators in JavaScript, but it's actually a bit complicated with all the edge cases, so here's just a link to the documentation for && and || for those who are interested (there are also other logical operators).

In order to make logical operations work really logically, we need to feed them operands that are actual boolean, i.e. true or false by themselves, and not thanks to some background magic. Otherwise, sooner or later you will find yourself debugging the application in disbelief and wondering why the condition isn't evaluated as you expected.

If you're worried that this means going back to a "chatty" explicit comparison with several possible (non)values, do not despair. We have a trick for that. Simply rewrite it as:

console.log(!!text1 && !!text2)
console.log(!!text1 || !!text2)
Enter fullscreen mode Exit fullscreen mode

It works! But why? 👀

Don't worry, I had a "WTF?" moment when I first saw this too. But there is nothing magical about double negation. Again, the JavaScript starts by evaluating the truthiness of the text1 variable, and since 'text1' is there, it has the value of true internally. Now we negate it with the first ! and then we negate with the second ! again. Maybe you remember from mathematics - "The product of two negatives is a positive".

If it were the other way around and text1 was undefined, null or an empty string, JavaScript would first say false, the first negation would make true and the second would finally return false.

Using the concatenation of two logical NOTs !!, we get a simple, universal and always functional conversion of JavaScript values into boolean expressions, which can then be safely used and chained for logical evaluation of conditions. Keep this in mind when writing your JS code and be prepared to look for incorrect usages if you seek for bugs in existing programs.

Have you ever encountered this in your code or codes you had to debug? Share your experience in the comments!

Top comments (2)

Collapse
 
lebbe profile image
Lars-Erik Bruce • Edited

I love this feature of JavaScript, but there are some gotchas developers should be aware of:

1) Empty arrays are truthy, one would easily have thought otherwise

!![]
true
Enter fullscreen mode Exit fullscreen mode

2) NaN is falsy. Instead of trying to check if a number is NaN, check if a number is falsy. this could be very handy in certain circumstances, except for that

3) The number 0 is falsy. Yep thats right, so if you use an if (number) { ...} to safeguard against NaN, you won't perform any computations on 0 as well.

In JSX, there is also an issue with conditions not being boolean, say in usages like this:

<div>
  {someVariable && <Component someProp={someVariable} />}
</div>
Enter fullscreen mode Exit fullscreen mode

If, somehow, someVariable is NaN here, the letters "NaN" would show up on the screen! I'm surpriced how often I've actually spotted "bugs" like this in applications I have maintained. Often they have gone unnoticed because it's been barely visible, or just been a temporary state. In cases like these, you can use !! to enforce someVariable to become a boolean as well, and React won't render booleans to the screen:

<div>
  {!!someVariable && <Component someProp={someVariable} />}
</div>
Enter fullscreen mode Exit fullscreen mode
Collapse
 
aloisseckar profile image
Alois Sečkár • Edited

Thank you for pointing out those exceptions. I got caught by 0 being falsy couple of times as well.

And btw my personal favorite among falsy JS values is document.all, although one probably won't use it so often in the actual code.