Type coercion and ToPrimitive: how JavaScript converts values for you
When types do not match, JavaScript converts them to numbers, strings or booleans. Learn the rules, why + is special, and how objects become primitives.
Comes up in: JavaScript, Recruiter screen
Learn#
Ishaan’s cart adds a delivery fee read from a form field: subtotal + fee. With a ₹500 subtotal and a ₹40 fee, the page shows ₹50040. Form fields hold strings, and + with a string joins text instead of adding.
When an operator gets the “wrong” type, JavaScript converts it for you. That is coercion — implicit conversion, because you did not ask for it.
There are three targets. ToNumber (used by -, *, <, unary +): "" → 0, " 42 " → 42, "4px" → NaN, null → 0, undefined → NaN. ToString (used by + with a string and by template literals). ToBoolean (used by if, !, &&): only false, 0, -0, 0n, "", null, undefined and NaN are false — everything else, even "0" and [], is true.
Objects are first turned into a primitive with ToPrimitive, guided by a hint: "number", "string" or "default". An object can decide for itself with Symbol.toPrimitive:
console.log([1 + "2", "3" * "4", true + 1, [] + [], [] + {}, `${[1, 2]}`]);
const price = { [Symbol.toPrimitive](hint) { console.log("hint:", hint); return hint === "string" ? "₹99" : 99; } };
console.log(+price);
console.log(`${price}`);
console.log(price + 1);Output (verified)
[ '12', 12, 2, '', '[object Object]', '1,2' ] hint: number 99 hint: string ₹99 hint: default 100
Without it, the default tries valueOf() then toString(). Arrays and plain objects return themselves from valueOf, so toString wins: [] → "", {} → "[object Object]".
Template literals always use the string hint.
left
- []
right
- {}
result
empty
[] + {}: + needs primitives first. Each side goes through ToPrimitive with the hint 'default'.
Read the steps as text
- [] + {}: + needs primitives first. Each side goes through ToPrimitive with the hint 'default'.
- [].valueOf() returns the array itself — not a primitive. So toString() is tried: ''.
- {}.valueOf() is also the object. {}.toString() gives '[object Object]'.
- One side is a string, so + joins strings instead of adding numbers.
Predict#
Predict first
What does this print? Type it exactly.
console.log("5" - 2, "5" + 2);- only works on numbers, so "5" becomes 5 and the result is 3. + sees a string and joins: "52". The tempting “3 7” assumes + converts like - does.
Interview angle#
Say it like this — about 30 seconds
“Coercion converts values when an operator needs a different type. Arithmetic and comparisons use ToNumber, conditions use ToBoolean, and plus joins strings if either side is a string after conversion. Objects first go through ToPrimitive with a hint:
Symbol.toPrimitiveif defined, otherwisevalueOfthentoString— or the reverse for the string hint. That is why[] + {}is ‘[object Object]’: both become strings. I convert explicitly at boundaries, like form input.”
Follow-up What does [] + {} evaluate to and why?
valueOf returns the objects themselves, so toString gives ” and ‘[object Object]’, and + joins them.Follow-up Which values are falsy?
Follow-up How do you convert a form value to a number safely?
Number(value) and then check Number.isNaN — or Number.parseFloat if trailing units are allowed. Do not rely on + with strings.Practice#
On your own
Give Money a Symbol.toPrimitive method so it formats as text but calculates as rupees.
Done when
`${new Money(1250)}`and"Total: " + new Money(1250)give"₹12.50"-style text.+m,m * 2andm > 10use the rupee number.
Remember#
Try to answer each card before you open it. Then add them to your reviews.
When does + join strings instead of adding?
When either side is a string after ToPrimitive.
List the falsy values.
false, 0, -0, 0n, '', null, undefined, NaN.
What does ToPrimitive try for a plain object with the default hint?
valueOf, then toString (unless Symbol.toPrimitive is defined).
What does this print?console.log(Number(""), Number("4px"), Number(null));
console.log(Number(""), Number("4px"), Number(null));0 NaN 0
Go deeper
Four layers. Stop at the one your interview needs; each ends with a quick check.
The core ideaEvery round
Three conversions — to number, to string, to boolean — and one step before them for objects: ToPrimitive. + is the only arithmetic operator that prefers strings.
Check yourself: Is the string '0' truthy?
Yes. Only the empty string is falsy.
How it works underneathSenior rounds
ToPrimitive calls Symbol.toPrimitive if present; otherwise OrdinaryToPrimitive tries valueOf then toString for “number”/“default”, and toString then valueOf for “string” Source: ECMA-262: ToPrimitive. The + operator applies ToPrimitive to both sides, then joins strings if either is a string Source: ECMA-262: ApplyStringOrNumericBinaryOperator.
Check yourself: What order does OrdinaryToPrimitive use for the string hint?
toString first, then valueOf.
Edge cases and trapsWhere people slip
Date treats the default hint as “string”, so date + 1 joins text while date - 0 gives milliseconds. Symbol values throw when converted to a string implicitly. BigInt and Number cannot be mixed in arithmetic: 1n + 1 throws a TypeError (JS.05.04).
Check yourself: Which built-in object uses the string hint for +?
Date.
In productionStaff-level answers
Convert at the edges: parse form and query values once with Number(...) and validate. TypeScript allows string + number, so add the @typescript-eslint/restrict-plus-operands rule, and use Intl.NumberFormat for display (JS.05.04).
Check yourself: Which TypeScript setting stops adding a number to a string by accident?
None by default — use explicit conversion and lint rules like restrict-plus-operands.
Finished the five steps? Mark it, and its cards join your reviews.
Last checked 2026-10-31. Spot a mistake? Use “Report an error” below — fixes are listed on the corrections page.
Interview questions this answers
- What does
[] + {}evaluate to and why?