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:
- Get primitive values: The engine calls
ToPrimitiveon each operand with a hint of Number.ToPrimitivefirst attempts to invokevalueOf, and if that does not return a primitive, it falls back totoString. - Check for strings: If either resulting primitive is a string, the operation becomes string concatenation. Both primitives are converted to strings via
ToStringand joined. - Numeric addition: If neither primitive is a string, both are converted to numbers using
ToNumberand 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 + 4performs numeric addition, resulting in7. - Then,
7 + '5'encounters a string, coercing7to"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(tryingvalueOfthentoString) followed byToNumberorToStringto resolve operands. - Unlike
+, operators like-,*,/, and%always coerce to numbers, making them more predictable. - Understanding these rules, as documented in the
lydiahallie/javascript-questionsrepository (README.mdlines 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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →