This post explains a quiz originally shared as a LinkedIn poll.
🔹 The Question
const prices = [9.99, '4.99', 5.00];
const total = prices.reduce((sum, p) => sum + p, 0);
console.log(total);
console.log(typeof total);
console.log(total === 19.98);
Hint: The + operator has two jobs in JavaScript. What decides which one it picks? Watch what happens to the accumulator the moment it meets a string.
Follow me for JavaScript puzzles and weekly curations of developer talks & insights at Talk::Overflow: https://talkoverflow.substack.com/
🔹 Solution
Correct answer: B) 9.994.995, string, false
The output is:
9.994.995
string
false
🧠 How this works
The + operator in JavaScript is overloaded: it performs numeric addition when both operands are numbers, but switches to string concatenation the moment either operand is a string. This is defined by the ECMAScript spec's Abstract Addition operation — if ToPrimitive of either side produces a string, both sides are coerced to strings and concatenated.
Inside reduce, the accumulator sum starts as 0 (a number). The first iteration adds 9.99, keeping it numeric. But the second element '4.99' is a string. At that point, + switches to concatenation and the accumulator becomes a string. From there, every subsequent + operation continues to concatenate — even though 5.00 is a number, the left operand is already a string, so 5.00 is coerced to "5" and appended.
This is a textbook silent data corruption bug. No error is thrown, no warning is logged. The code runs to completion, but total holds a nonsensical string like "9.994.995" instead of the expected 19.98.
🔍 Line-by-line explanation
const prices = [9.99, '4.99', 5.00]— An array with three elements. The first and third are numbers. The second is a string — this is the trap. In production, this often happens when API responses, form inputs, URL parameters, orlocalStoragevalues return numeric data as strings.prices.reduce((sum, p) => sum + p, 0)— Starts reducing with an initial accumulator of0.Iteration 1:
sum = 0,p = 9.99— Both are numbers.0 + 9.99 = 9.99. The accumulator is9.99(number).Iteration 2:
sum = 9.99,p = '4.99'—sumis a number,pis a string. The+operator sees a string on the right side and chooses concatenation.9.99is coerced to"9.99", then concatenated with"4.99". The accumulator becomes"9.994.99"(string). This is the point of no return.Iteration 3:
sum = '9.994.99',p = 5.00—sumis now a string. Even thoughpis a number,+still chooses concatenation because the left operand is a string.5.00is coerced to"5"(trailing zero is dropped inNumber.toString()). The accumulator becomes"9.994.995".console.log(total)— Prints9.994.995. This looks like it could be a decimal number with two dots, but it's actually a string that makes no numeric sense.console.log(typeof total)— Printsstring. The type has silently changed from number to string mid-reduction.console.log(total === 19.98)—"9.994.995" === 19.98isfalse. Strict equality between a string and a number always returnsfalsewithout coercion.
The non-obvious part: The + operator's switch from addition to concatenation is irreversible within the reduce chain. Once a single string element flips the accumulator to a string, every subsequent iteration concatenates — even for numeric elements. There is no error, no NaN, no indication that anything went wrong. The code silently produces garbage.
🔹 Key Takeaways
The
+operator picks concatenation over addition if either operand is a string. This is asymmetric — once a string appears, the operation flips irreversibly in a reduce chain.Always coerce values before arithmetic. Use
Number(p),parseFloat(p), or the unary+poperator inside your reducer:prices.reduce((sum, p) => sum + Number(p), 0).API data types are a contract, not a guarantee. Validate and parse numeric fields at the boundary where external data enters your application — never assume the type is correct.
This bug produces no errors. Unlike most type-related issues,
+coercion never throws. The result is valid JavaScript — it's just not the value you expected. This makes it one of the hardest bugs to catch without explicit type checks or TypeScript.typeofchecks or runtime validation in reducers can act as guardrails. In critical financial calculations, consider asserting that every element is a number before reducing, or use a library that enforces decimal arithmetic.
Top comments (0)