When strict equality goes wrong
I recently saw this code in the wild. Names have been changed to protect those involved.
function makeFizzbobs (widget) {
if (widget === null) {
return {};
} else {
// Does something interesting with widget
return 42;
}
}
I could imagine what the author of the code thought they were doing: they were guarding against an empty function parameter right? Cos null sounds like what you’d be left with if the user didn’t supply an argument like floop
or 321
. And all this linting software tells you to use triple equals instead of doubles so it must be right? User passes something in: carry on with the function, user doesn’t pass something to the function: we return an empty object. Job done.
🚨 No.
This is what actually happens:
makeFizzbobs(); // 42
Oh. So an empty argument isn’t the same thing as null
? 🤔 No. No it’s not. Well what about an empty string, or maybe that undefined
value, that sounds empty:
makeFizzbobs(''); // 42
makeFizzbobs(undefined); // 42
makeFizzbobs(false); // 42
makeFizzbobs(null); // {}
So it turns out that null only strictly equals itself, or null === null
. Unsurprisingly, null !== undefined
or any of the other values we tried above.
In fact, when you call the method with no arguments, widget
gets the value undefined
(add a console.log(widget)
in the method to see for yourself). So how can we fix this function to guard against an empty argument? We could change the check in the if-statement from null
to undefined
, but then we’ll let null
s through instead.
Maybe you’d think that we could have a long if-clause, like:
if (widget === null || widget === undefined || widget === ... ) {
But there is a better way!
You can play along at home by whipping up some quick unit tests like I outlined in my other blog post. Define the makeFizzbobs
function at the top of the test file and write some test cases.
Personally, I would short-circuit the function execution by checking for a falsey. There are 6 falsey values in JavaScript, and it’s likely that you aren’t interested in any of them:
- false
- NaN
- 0
- undefined
- null
- “
A falsey evaluates to false
in an if-clause, which we can use like so:
function makeFizzbobs (widget) {
if (widget) {
// Do something exciting
return 42;
} else {
return {};
}
}
You may notice that I’ve swapped the order around, so I’m dealing with desirable input first and then the falsey is dealt with in the else
clause. This is because I think the if-clause reads a little clearer and is easier to comprehend this way, rather than having to reverse the logic in your head to work out what !widget
means (I’m lazy, so what 😴).
So now the function works like:
makeFizzbobs(); // {}
makeFizzbobs(null); // {}
makeFizzbobs(''); // {}
makeFizzbobs(123); // 42
makeFizzbobs('abc'); // 42
Of course, this may not be suitable for your needs: perhaps you want to do something really interesting with an empty string ''
or you just want to deal with 0
like any other number, or perhaps you really don’t want an empty array passed in, or Infinity
, or the string "flibbertigibbet"
, but this is a good place to start in JavaScript’s loosely typed Wild West, and a good reminder that things are never what they seem with type checking in JavaScript.