JavaScript Type Conversions
Learn how JavaScript converts values between strings, numbers, and booleans — explicit conversions and implicit coercion rules.
JavaScript is a dynamically typed language: a variable can hold a string at one moment and a number the next, and you never declare a value's type ahead of time. Because of this flexibility, values constantly need to move between types — a number becomes text to be printed, a form field's text becomes a number for a calculation, an object is tested for "truthiness" inside an if. These conversions happen in two ways: explicitly, when you call a function like String() or Number() yourself, and implicitly (also called coercion), when the language converts a value automatically to fit the operation. This chapter covers both, the exact rules for each target type, and the surprises that trip up almost everyone at some point.
If you have not yet read about the available data types, it helps to skim that first — conversions only make sense once you know what you are converting between.
The Three Conversions
Almost every conversion in JavaScript targets one of three primitive types: string, number, or boolean. There is no general "convert to type X" operation; instead each target has its own rules. We will look at each in turn, then see when the language applies them for you.
String Conversion
String conversion turns a value into its text form. It happens explicitly with the String() function, and implicitly whenever a value meets a string in concatenation or is interpolated into a template literal.
String(123); // "123"
String(true); // "true"
String(null); // "null"
String(undefined); // "undefined"
`Value: ${42}`; // "Value: 42"The rules are intuitive for primitives — the value is rendered exactly as you would expect to read it. Note that null and undefined become the literal text "null" and "undefined", not an empty string.
Objects and arrays are more interesting. An array becomes its elements joined by commas, and a plain object becomes the famously unhelpful "[object Object]":
So an empty array stringifies to an empty string, and null/undefined inside an array render as empty slots (so [null, 5] gives ",5"). The "[object Object]" result for objects is why you reach for JSON.stringify(obj) when you actually want to read an object's contents.
Numeric Conversion
Numeric conversion turns a value into a number. The explicit form is the Number() function; implicitly it happens with the unary plus operator (+value) and with most arithmetic and comparison operators.
The rules are worth memorizing because they are not always obvious:
| Value | Number(value) |
|---|---|
undefined | NaN |
null | 0 |
true / false | 1 / 0 |
"" (empty string) | 0 |
" 42 " (whitespace around digits) | 42 |
"42px" (non-numeric content) | NaN |
"3.14" | 3.14 |
A few of these are notorious. Number(null) is 0 but Number(undefined) is NaN — the two "empty" values behave differently. An empty string also becomes 0, not NaN. Strings are trimmed of surrounding whitespace first, then parsed; if anything non-numeric remains, the result is NaN (which stands for "Not a Number").
Unary Plus
The unary plus operator is the shortest way to force a numeric conversion. Placed before a value, +value does exactly what Number(value) does:
+"42"; // 42
+""; // 0
+true; // 1
+"abc"; // NaNThis is handy but easy to misread, so reserve it for places where the intent is clear (such as +event.target.value when reading a numeric form field).
parseInt and parseFloat vs Number
Number() is strict: the entire string must be a valid number, or you get NaN. The parseInt() and parseFloat() functions are forgiving — they read from the start of the string and stop at the first character that does not fit, returning whatever they parsed so far.
Two cautions with parseInt:
- It returns
NaNonly if the string does not start with a parseable number —"px12"fails, but"12px"succeeds with12. - Always pass the radix (base) as the second argument to avoid surprises:
parseInt("08", 10)gives8reliably. Without it, modern engines default to base 10, but being explicit removes any doubt and documents your intent.
parseInt("FF", 16); // 255 (hexadecimal)
parseInt("101", 2); // 5 (binary)
parseInt("08", 10); // 8 (decimal, explicit radix)Use Number() (or +) when you want to validate that a whole value is numeric, and parseInt/parseFloat when you deliberately want to extract a number from the front of a larger string, such as "24px" from CSS.
Boolean Conversion
Boolean conversion answers a single question: is this value "truthy" or "falsy"? It happens explicitly with Boolean() and implicitly anywhere a condition is evaluated — inside if, while, the ternary ?, and logical operators.
The rule is refreshingly simple because the list of falsy values is short and fixed. These eight values convert to false:
false
0
-0
0n // BigInt zero
"" // empty string
null
undefined
NaNEverything else is truthy. That "everything else" includes some values that surprise newcomers:
The string "0" is truthy because it is a non-empty string — its contents do not matter. Likewise "false" is a perfectly normal non-empty string and converts to true. And an empty array [] or empty object {} is truthy, even though it "feels" empty. Memorize the falsy list; if a value is not on it, it is truthy.
A common idiom for an explicit boolean is the double-NOT: !!value. The first ! converts to a negated boolean, the second flips it back, giving you the same result as Boolean(value) in fewer characters. For example, !!"hello" is true and !!0 is false.
Implicit Coercion
So far we have called the conversion functions ourselves. Most real-world conversions, though, are implicit — the language coerces values to make an operation work. The trick is knowing which target type each context wants.
Arithmetic and the + Quirk
Every arithmetic operator coerces its operands to numbers — except +, which is overloaded. If either operand of + is a string, + becomes string concatenation; otherwise it adds numbers. Every other operator (-, *, /, %) always converts to numbers.
The line 1 + 2 + '3' shows why order matters: + is left-associative, so 1 + 2 runs first as numbers (3), then 3 + '3' hits a string and concatenates to "33". This single rule — "+ with any string concatenates, everything else converts to number" — explains a large share of beginner confusion.
Conditions and Logic
In any boolean context — if, while, ?:, and the logical operators && / || — the controlling value is run through boolean conversion using the falsy list above.
if ("0") {
// runs: "0" is a non-empty string, therefore truthy
}
if (0 || "fallback") {
// "fallback" is returned: 0 is falsy, so || moves to the next value
}Comparisons
The equality operators do their own coercion, and the loose == operator in particular has rules subtle enough to deserve their own chapter. The short version: prefer strict ===, which never coerces. The full set of surprises (such as [] == ![] being true) is covered in comparison operators.
Object-to-Primitive Conversion
When an object is used where a primitive is expected (concatenation, arithmetic, an alert message), JavaScript converts it to a primitive first. It does this by calling the object's Symbol.toPrimitive method if one exists; otherwise it falls back to valueOf() and toString(), choosing the order based on whether a number or a string is "hinted." This is why a plain object turns into "[object Object]" and an array into a comma-joined string — those come from the default toString(). You can customize the result by defining these methods on your own objects, but for everyday code it is enough to know the conversion exists and to use JSON.stringify() when you want a readable object.
Common Pitfalls
A handful of conversion results account for most "wait, what?" moments. Keep these in mind:
Walking through the odd ones:
+[]is0. Numeric conversion first turns the array into a string (""), then the empty string converts to0.+{}isNaN. The object becomes"[object Object]", which is not a valid number, so it converts toNaN."" + 1is"1". Because+met a string, it concatenated instead of adding.[] + []is"". Each array stringifies to an empty string, and"" + ""is"".
These are not bugs — they follow the rules above exactly. They only feel strange because two different conversions chain together in a single expression.
Best practice: prefer explicit conversion. Writing Number(input), String(value), or Boolean(flag) makes your intent obvious to the next reader and sidesteps the surprises of implicit coercion. Reserve implicit conversion for cases that are unmistakably clear, and lean on strict === so values are never silently changed during comparisons.
Summary
- JavaScript converts values to string, number, or boolean — explicitly via
String(),Number(),Boolean(), or implicitly through coercion. - String: primitives render as their text;
[1,2,3]becomes"1,2,3",[]becomes"", and a plain object becomes"[object Object]". - Number:
nullis0,undefinedisNaN,""is0; strings are trimmed then parsed strictly. Use+valueas shorthand, andparseInt/parseFloat(with a radix) for lenient front-of-string parsing. - Boolean: only
false,0,-0,0n,"",null,undefined, andNaNare falsy; everything else — including"0","false",[], and{}— is truthy. +concatenates when either side is a string; every other arithmetic operator converts to a number.- When in doubt, convert explicitly and compare with
===.
You will use these rules constantly when working with numbers, strings, and user input stored in variables.