JavaScript & TypeScript DISCUSSION

== vs === in JavaScript: what exactly does loose equality convert, and when is it safe?

Started by hind strict equalityloose equalitytype coercioninput validationNaN comparison
5 replies 248 views 6 participants
Latest activity · 30 Sep 2026

== vs === in JavaScript: what exactly does loose equality convert, and when is it safe?

#1

A form on my configuration page reads a channel number from an input box and compares it with if (channel == 0). The branch is taken not only for "0" but also when the box is left empty, which disables the wrong output. With === the branch is never taken at all, even when I type 0.

I know the advice is to always use ===, but I would like to understand what == actually does, why an empty string equals zero, and how to compare user input with numbers correctly.

Community replies 5

Re: == vs === in JavaScript: what exactly does loose equality convert, and when is it safe?

#2

=== compares without converting: if the types differ, the result is false. input.value is always a string, so "0" === 0 is false, and that is why your strict version never takes the branch.

== first converts the operands to a common type by fixed rules. String against number: the string is converted to a number. A boolean is converted to a number first. An object against a primitive is converted through valueOf or toString. null and undefined equal each other and nothing else. The string-to-number conversion treats an empty or whitespace-only string as 0, so "" == 0 becomes 0 == 0, which is true. That is your empty box.

Re: == vs === in JavaScript: what exactly does loose equality convert, and when is it safe?

#3

The fix is to convert explicitly, once, and then compare strictly. Read const text = input.value.trim();, reject it if text === '', then const channel = Number(text); and reject it unless Number.isInteger(channel) && channel >= 0 && channel <= 7. After that, channel === 0 means what it says.

The conversions differ in ways worth knowing. Number("") is 0, hence the explicit empty check. Number("12px") is NaN, whereas parseInt("12px", 10) is 12, because parseInt stops at the first character that is not a digit. Always pass the radix. For a numeric input element the browser also offers input.valueAsNumber, which is NaN when the box is empty.

Re: == vs === in JavaScript: what exactly does loose equality convert, and when is it safe?

#4

Some results that follow from the rules. 0 == "" and 0 == "0" are both true, but "" == "0" is false, since two strings are compared without conversion; so == is not even transitive. false == "0" is true, although "0" is truthy in an if. null == 0 is false, yet null >= 0 is true, because relational operators convert null to 0 and equality does not.

NaN == NaN is false with either operator; test with Number.isNaN(x). And objects, including arrays and dates, compare by identity with both operators: [1, 2] === [1, 2] is false.

Re: == vs === in JavaScript: what exactly does loose equality convert, and when is it safe?

#5

There is one widely accepted use of ==: x == null is true exactly for null and undefined, and false for 0, an empty string, false and NaN. It is a compact test for "has no value", and ESLint's eqeqeq rule has an option that permits just that case.

The modern operators cover the same ground. x ?? fallback uses the fallback only for null and undefined, unlike x || fallback, which also replaces 0 and the empty string. That is a classic bug when 0 is a valid channel or a valid PWM duty. obj?.prop yields undefined instead of throwing when obj is null or undefined.

Re: == vs === in JavaScript: what exactly does loose equality convert, and when is it safe?

#6

A few related comparison rules. switch uses strict comparison, so switch (input.value) { case 0: ... } never matches a string. indexOf and includes on arrays are strict too, except that includes can find NaN. Object.is(a, b) behaves like === except that NaN equals itself and +0 differs from -0.

Neither operator helps with floating point: 0.1 + 0.2 === 0.3 is false, so compare computed values with a tolerance. If the project can use TypeScript, it reports a comparison between a string and a number as an error because the types have no overlap, which would have pointed at this bug before the page was ever loaded.

TEP COMMUNITY