In With Primitive
ID |
javascript.in_with_primitive |
Severity |
critical |
Remediation Complexity |
trivial |
Remediation Risk |
low |
Remediation Effort |
low |
Resource |
Reliability |
Language |
JavaScript |
Tags |
reliability, runtime-error, suspicious-comparison |
Description
Reports x in y where y is a primitive value — a string, a number, a boolean, null or undefined. The in operator is specified to throw a TypeError unless its right operand is an object, so the expression never produces true or false: it aborts the surrounding function.
if ("length" in "abc") { } // FLAW — TypeError: Cannot use 'in' operator to search for 'length' in abc
if (0 in 42) { } // FLAW
const NAME = "widget";
if ("id" in NAME) { } // FLAW — NAME is a const holding a string
Two operand shapes are recognised and nothing else: a literal written on the spot, and an identifier that resolves to a const binding initialised with such a literal. const is what makes the second case sound — the value can no longer be replaced by an object further down. A let or a var may hold a string on the declaration line and an object at the line being judged, so it is left alone.
Rationale
-
The line is dead on arrival. Unlike a comparison that quietly comes out false, this one throws, so every path that reaches it fails.
-
The mistake is nearly always a confusion with another operation.
"b" in "abc"was meant to be"abc".includes("b");"id" in recordwas meant to run against an object and the variable holding it turned out to be a primitive. -
for (const key in "abc")is a different construct and does coerce, so nothing about the loop form warns the author that the operator form does not.