Search code examples
javascriptnode.jscomparisonthrowoperands

a.b < 0, why does this not throw an error when a.b === undefined?


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.


Solution

  • See the specification: what it does is:

    1. 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:

    1. If x is NaN, return undefined.

    And then, coming back to the step after the 5. at the very beginning of the answer:

    1. 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.