A typo ended up as
a.b > 0
with a.b
undefined. According to MDN (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comparison_Operators#Greater_than_operator) the comparison calls .valueOf()
on the operands. So it should be
a.b.valueOf() > 0
but that actually throws an error which does not seem to happen. Instead this seems to be evaluated:
undefined > 0
Am I misunderstanding something?
In my case this led to a silent error in the Node.js code. I certainly wanted that an error was thrown instead.
Can I handle this without explicitly checking the operands?
EDIT: freeCodeCamp seems to describe this more correct than MDN:
https://guide.freecodecamp.org/javascript/comparison-operators/
But I still wonder about the most easy way to handle this without being trapped by simple typos.
See the specification: what it does is:
- Let r be the result of performing Abstract Relational Comparison lval < rval.
which does, which tries to coerce each side to a primitive first:
a. Let px be ? ToPrimitive(x, hint Number).
b. Let py be ? ToPrimitive(y, hint Number).
undefined
is already a primitive - nothing throws. Then, it does:
Let nx be ? ToNumeric(px).
Let ny be ? ToNumeric(py).
Casting undefined
to a Number doesn't throw either, but it returns NaN
:
console.log(Number(undefined));
So then we get to
If Type(nx) is the same as Type(ny), return Type(nx)::lessThan(nx, ny).
where nx
is NaN
and ny
is 0. lessThan
returns undefined
when nx
is NaN
, described here:
- If x is NaN, return undefined.
And then, coming back to the step after the 5.
at the very beginning of the answer:
- If r is undefined, return false. Otherwise, return r.
The result is undefined
, so false
is returned (without an error being thrown).
So it should be
a.b.valueOf() > 0
Only an object will have valueOf
called on it in the process of <
evaluation (in the a. Let px be ? ToPrimitive(x, hint Number).
above). undefined
is not an object - it's already a primitive, so no additional conversion happens at that step.
I certainly wanted that an error was thrown instead.
Can I handle this without explicitly checking the operands?
Not really, but that shouldn't be hard to do at all - just check that a.b
is a Number first if you aren't sure. if (typeof a.b === 'number')
. Nothing wrong with that.