JavaScript Type Coercion with the `+` Operator: A Complete Guide

The + operator performs string concatenation if either operand is a string; otherwise, it converts both operands to numbers and adds them, following the ECMAScript Abstract Operations ToPrimitive and ToNumber.

JavaScript type coercion with the + operator is one of the most common sources of confusion in the language. According to the lydiahallie/javascript-questions repository, this behavior is explicitly tested in question 15, where the interaction between numbers and strings reveals how the engine prioritizes string concatenation over arithmetic addition.

How the + Operator Decides Between Addition and Concatenation

The ECMAScript specification defines the behavior of the + operator through the Abstract Operation AdditiveExpression. This operation is unique because it is the only binary operator in JavaScript that handles both numeric arithmetic and string concatenation.

The ToPrimitive Conversion Process

When the JavaScript engine encounters the + operator, it follows these steps:

  1. Get primitive values: The engine calls ToPrimitive on each operand with a hint of Number. ToPrimitive first attempts to invoke valueOf, and if that does not return a primitive, it falls back to toString.
  2. Check for strings: If either resulting primitive is a string, the operation becomes string concatenation. Both primitives are converted to strings via ToString and joined.
  3. Numeric addition: If neither primitive is a string, both are converted to numbers using ToNumber and added mathematically.

Why Strings Win Over Numbers

The precedence rule is simple: the string case always wins. This means that if any operand in a + expression evaluates to or can be coerced into a string, the entire operation becomes concatenation.

This left-to-right associativity creates counterintuitive results. For example, 3 + 4 + '5' evaluates to '75' because:

  • First, 3 + 4 performs numeric addition, resulting in 7.
  • Then, 7 + '5' encounters a string, coercing 7 to "7" and concatenating to "75".

JavaScript Type Coercion Examples with the + Operator

The lydiahallie/javascript-questions repository demonstrates these concepts in README.md (lines 491-512), showing how implicit coercion affects real-world code.

// Numeric addition (both operands coerce to numbers)
console.log(2 + 3);               // 5
console.log(2 + true);            // 3 (true → 1 via ToNumber)
console.log(2 + null);            // 2 (null → 0)

// String concatenation (string wins)
console.log('2' + 3);             // "23"
console.log(2 + '3');             // "23"
console.log(3 + 4 + '5');         // "75"

// Object coercion via valueOf/toString
console.log([] + []);             // "" (both become empty strings)
console.log([] + {});             // "[object Object]" (array → "", object → "[object Object]")
console.log({} + []);             // 0 (object → NaN, array → 0, but spec treats {} as 0 in this context)

// Explicit coercion for predictable results
const a = 2;
const b = '3';
console.log(String(a) + b);       // "23"
console.log(Number(b) + a);       // 5

Edge Cases and Primitive Conversion

Understanding how different primitives and objects coerce is essential for debugging unexpected + operator behavior.

Expression Result Reason
1 + true 2 Boolean true coerces to 1 via ToNumber.
1 + null 1 null coerces to 0.
1 + undefined NaN undefined coerces to NaN, poisoning the addition.
{} + [] 0 Both become primitives: {} → "[object Object]" → NaN, [] → "" → 0; numeric addition results in NaN + 0 = NaN, but the spec interprets {} as a block in statement context, yielding 0.
[] + {} "[object Object]" [] → "" (string), {} → "[object Object]"; string concatenation wins.

How Other Arithmetic Operators Handle Coercion

Unlike the + operator, all other arithmetic operators (-, *, /, %) consistently coerce their operands to numbers using ToNumber, regardless of whether strings are present.

// Subtraction always coerces to numbers
console.log('5' - 2);     // 3 (string "5" → number 5)
console.log('5' - '2');   // 3 (both strings → numbers)

// Multiplication follows the same rule
console.log('4' * '2');   // 8
console.log('4' * 2);     // 8

// Division and modulo
console.log('10' / '2');  // 5
console.log('10' % '3');  // 1

This distinction makes the + operator uniquely prone to type coercion surprises in JavaScript codebases.

Summary

  • The + operator is dual-purpose, performing either numeric addition or string concatenation based on operand types.
  • String coercion takes precedence: if either operand is a string (or becomes one via ToPrimitive), the operation concatenates.
  • The engine uses ToPrimitive (trying valueOf then toString) followed by ToNumber or ToString to resolve operands.
  • Unlike +, operators like -, *, /, and % always coerce to numbers, making them more predictable.
  • Understanding these rules, as documented in the lydiahallie/javascript-questions repository (README.md lines 491-512), helps prevent common bugs in JavaScript applications.

Frequently Asked Questions

What is the difference between JavaScript type coercion and type conversion?

Type coercion refers to the automatic, implicit conversion of values from one data type to another by the JavaScript engine, such as when using the + operator with mixed types. Type conversion (or type casting) is the explicit, manual conversion performed by developers using functions like Number(), String(), or Boolean(). While both achieve similar outcomes, coercion happens automatically during operations, whereas conversion requires intentional code.

Why does {} + [] return 0 but [] + {} returns "[object Object]"?

This discrepancy occurs due to how JavaScript interprets the leading {} in statement context versus expression context. In {} + [], the engine treats the {} as an empty block statement (not an object literal), leaving + [] which coerces the empty array to 0. In [] + {}, both operands are expressions; the array becomes an empty string "" via toString(), the object becomes "[object Object]", and string concatenation produces the final result.

How can I avoid unexpected type coercion with the + operator?

To prevent unintended coercion, explicitly convert operands before using the + operator. Use Number() or the unary + operator to force numeric addition (e.g., Number('5') + 3), or use String() to ensure concatenation (e.g., String(5) + ' items'). Additionally, consider using template literals for string composition, as they implicitly convert values to strings without the ambiguity of the + operator.

Does the + operator always prefer string concatenation over numeric addition?

Yes, when ambiguity exists. If either operand is a string primitive, or if an object operand coerces to a string via ToPrimitive (specifically when toString() returns a primitive before valueOf() is tried), the + operator performs string concatenation. Only when both operands resolve to non-string primitives (numbers, booleans, null, undefined coerced to numbers) does the operator perform numeric addition. This "string wins" rule is hardcoded in the ECMAScript specification's AdditiveExpression abstract operation.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →