DEV Community

Parth
Parth

Posted on

Why "1" + 2 + 2 is "122" but 1 + 2 + "2" is "32"

This is the third post in a series I'm writing as I learn JavaScript.

Here are two lines that look almost identical:

console.log("1" + 2 + 2); // "122"
console.log(1 + 2 + "2"); // "32"
Enter fullscreen mode Exit fullscreen mode

Same numbers. Same +. The only difference is where the string sits. And yet the answers aren't just different — they're different kinds of thing. One is a hundred and twenty-two-ish. The other is thirty-two-ish. Both are actually text.

When I first saw this I assumed it was a trick question. It isn't. Once you know the two rules behind it, it's completely predictable — and it stops being a source of bugs.

Here are the rules.

Rule 1: JavaScript reads left to right

JavaScript doesn't look at the whole line and plan ahead. It works through it one step at a time, left to right, and each + is evaluated before moving to the next.

So the line isn't judged all at once. It's a chain.

Rule 2: + has two completely different jobs

The + operator does one of two things depending on what it's touching:

  • Two numbers → addition. 2 + 2 is 4.
  • Anything involving a string → joining (concatenation). "a" + "b" is "ab".

And here's the key part: the moment + touches a string, it converts the other side into a string too, then glues them together. This automatic conversion has a name — type coercion. JavaScript coerces one value into another type to make the operation work.

Now let's walk through both lines with those two rules.

"1" + 2 + 2

Left to right:

"1" + 2     → "1" touches a string operation → 2 becomes "2" → "12"
"12" + 2    → still a string → 2 becomes "2" → "122"
Enter fullscreen mode Exit fullscreen mode

Result: "122". The string at the front poisons the whole chain. Once step one produces a string, every step after it is also a string.

1 + 2 + "2"

Left to right:

1 + 2       → two numbers → real addition → 3
3 + "2"     → now a string appears → 3 becomes "3" → "32"
Enter fullscreen mode Exit fullscreen mode

Result: "32". Here the numbers get to add up first, because the string doesn't show up until the end. 1 + 2 becomes 3, and only then does the "2" turn it into text.

That's the whole mystery. Same pieces, different order, and left-to-right evaluation does the rest. The string acts like a switch: everything after it becomes text.

The other operators aren't so confused

+ is the troublemaker because it has two jobs. Every other math operator has only one — math — so they don't get tempted to join strings. Instead, they convert strings into numbers:

console.log("5" - 2);  // 3   ("5" becomes 5)
console.log("10" - "4"); // 6  (both become numbers)
Enter fullscreen mode Exit fullscreen mode

- can't join anything, so when it meets a string it does the only thing it knows: turn it into a number and subtract. Same for * and /.

So there's a rough rule of thumb:

  • + leans toward strings — if it sees one, it joins.
  • -, *, / lean toward numbers — they always convert to number and do math.

When conversion fails: NaN

If you ask JavaScript to turn something into a number and it can't, you don't get an error. You get NaN — "Not a Number."

console.log(Number("Parth")); // NaN
console.log("Parth" - 2);     // NaN
Enter fullscreen mode Exit fullscreen mode

"Parth" isn't a valid number, so the conversion fails, and NaN is the result. The strange part: NaN's type is still number.

console.log(typeof NaN); // "number"
Enter fullscreen mode Exit fullscreen mode

It's a number-typed value that represents "this was supposed to be a number, but the math didn't work out." And you can't test for it with ===, because — famously — NaN isn't even equal to itself. You check for it like this:

console.log(Number.isNaN(NaN)); // true
Enter fullscreen mode Exit fullscreen mode

Two kinds of conversion

Everything above is JavaScript converting types on its own, mid-operation. That's called implicit conversion (or type coercion) — the engine does it for you, whether you asked or not.

You can also convert explicitly — on purpose, in your own code:

let age = "25";           // a string
let realAge = Number(age); // you convert it yourself

console.log(realAge);        // 25
console.log(typeof realAge); // "number"
Enter fullscreen mode Exit fullscreen mode

Implicit is JavaScript deciding for you. Explicit is you deciding for JavaScript. Bugs tend to come from the first kind — the conversions you didn't realize were happening.

The one that bites everyone: == vs ===

This is the most important practical takeaway, and it comes straight out of coercion.

== compares values after coercing them to the same type:

console.log(2 == "2"); // true — "2" is coerced to 2 first
Enter fullscreen mode Exit fullscreen mode

=== compares values without any coercion. Different type means not equal, full stop:

console.log(2 === "2"); // false — number vs string, done
Enter fullscreen mode Exit fullscreen mode

== is doing the same silent conversion that produced "122" — and that's exactly why it causes bugs. It says two things are equal when they only look equal after JavaScript quietly reshapes one of them.

Use === by default. It compares what's actually there, instead of what things turn into.

What I actually took away

For a while I thought JavaScript was just being weird on purpose. It isn't. Every one of these results comes from two simple rules — left to right, and + prefers strings — plus the fact that the language would rather convert a type than throw an error.

That last habit is the real theme. JavaScript almost never refuses. Give it a string where it wanted a number and it'll convert, join, or hand back NaN — anything but stop. That's convenient right up until it produces "122" when you wanted 5.

You can't turn the behavior off. But once you can see it coming, it stops being magic and starts being rules. And === quietly shuts down a whole category of it.


What's the coercion result that surprised you most? Mine was typeof NaN being "number". I'd love to hear yours in the comments.

Top comments (0)