CodeMIRAI
0 XP

Type conversion

5 min read 10 runnable examples Quiz included

A value's type decides what an operator does with it. Often you have the right value in the wrong type: the age someone typed is the text "21", not the number 21; a price needs to be shown as text with a currency sign. Type conversion turns a value of one type into another. You can do it on purpose (explicit conversion), and JavaScript also does it by itself in some situations (implicit conversion, called coercion). Knowing both stops a whole family of bugs.

Converting to a string

JavaScript
const n = 42;
console.log(String(n), typeof String(n));
console.log(n.toString(), (3.5).toString());
console.log(`${n}`);                    // a template literal converts too
console.log(String(null), String(undefined), String(true), String([1, 2]));

String(x) works on everything. x.toString() works on most values but fails on null and undefined (they have no methods), so prefer String(x) when the value might be missing. Arrays convert to their items joined by commas.

Converting to a number

Number(x) converts the whole value, or gives NaN if it can't:

ValueNumber(value)Why
"42"42a clean number
" 42 "42spaces at the ends are ignored
"4.5"4.5decimals work
""0an empty string is 0 (a common trap)
"42px"NaNnot entirely a number
true / false1 / 0
null0
undefinedNaN
JavaScript
console.log(Number("42"), Number("  42  "), Number("4.5"));
console.log(Number(""), Number("42px"), Number("abc"));
console.log(Number(true), Number(false), Number(null), Number(undefined));
console.log(+"7" + 1);         // unary + does the same as Number()

parseInt and parseFloat

parseInt and parseFloat read as much as they can from the start of a string and stop at the first character that doesn't fit. That makes them right for values with units, and wrong when you want to reject bad input:

JavaScript
console.log(parseInt("42px"), parseFloat("3.5kg"));
console.log(parseInt("12.9"));        // 12: parseInt stops at the dot
console.log(parseFloat("12.9"));      // 12.9
console.log(parseInt("abc42"));       // NaN: no digits at the start
console.log(parseInt("101", 2));      // 5: read "101" as binary
console.log(parseInt("ff", 16));      // 255: read as hexadecimal

The second argument of parseInt is the radix, the number base. It's good practice to always give it, usually 10: parseInt(text, 10).

UseWhen
Number(x)the whole string must be a number (form input, data)
parseInt(x, 10)you want the whole-number part of "42px" or "12.9"
parseFloat(x)you want a decimal from "3.5kg"

Converting to a boolean

Boolean(x) (or !!x) gives false for the eight falsy values (false, 0, -0, 0n, "", null, undefined, NaN) and true for everything else:

JavaScript
console.log(Boolean(""), Boolean("hi"), Boolean(0), Boolean(-3));
console.log(Boolean(null), Boolean([]), Boolean({}), Boolean("false"));
console.log(!!"text", !!0);

The surprising ones: "false" and "0" are non-empty strings, so they are true, and empty arrays and objects are true too.

When JavaScript converts by itself

+ joins as soon as a string is involved

If either side of + is a string, the other side becomes a string and they are joined. Every other arithmetic operator converts to numbers:

JavaScript
console.log(1 + "2");        // "12"
console.log("3" - 1);        // 2
console.log("3" * "4");      // 12
console.log("10" / "4");     // 2.5
console.log(true + 1);       // 2: true becomes 1
console.log("5" + 5 - 5);    // "55" - 5 = 50

Expressions run left to right, so "5" + 5 - 5 first joins ("55"), then subtracts (50).

Comparisons

<, >, <= and >= compare two strings alphabetically, but if one side is a number, they compare numerically:

JavaScript
console.log("10" < "9");     // true: as text, "1" comes before "9"
console.log("10" < 9);       // false: one side is a number, so 10 < 9
console.log(Number("10") < Number("9"));   // false: convert both to be sure

== converts, === doesn't

The loose equality == converts types before comparing, with rules that are hard to remember. The strict === never converts:

JavaScript
console.log("5" == 5, "5" === 5);
console.log(0 == "", 0 === "");
console.log(0 == false, null == undefined, null == 0);
console.log(NaN == NaN, NaN === NaN);

Use === and !== everywhere. The only common use of == is x == null, which is true for both null and undefined, and even that is clearer written as x === null || x === undefined or with ??.

Checking for NaN

NaN isn't equal to anything, itself included, so x === NaN is always false. Use Number.isNaN:

JavaScript
const result = Number("hello");
console.log(result === NaN);          // false, always
console.log(Number.isNaN(result));    // true
console.log(isNaN("hello"));          // the old global isNaN converts first: true
console.log(Number.isNaN("hello"));   // the strict version doesn't: false

Validating input

Everything a user types arrives as a string. Convert it, then check it before using it:

JavaScript
function readAge(text) {
  const age = Number(text.trim());
  if (text.trim() === "" || !Number.isInteger(age)) return "Please type a whole number";
  if (age < 0 || age > 120) return "That age doesn't look right";
  return `Next year you'll be ${age + 1}`;
}
for (const input of ["21", " 21 ", "", "21.5", "twenty", "-4"]) {
  console.log(JSON.stringify(input), "->", readAge(input));
}

Number.isInteger rejects decimals and NaN; Number.isFinite accepts any real number but rejects NaN and Infinity. The empty-string check matters because Number("") is 0.

The famous surprises, explained

ExpressionResultWhy
[] + []""both arrays become empty strings, then join
[] + {}"[object Object]"{} becomes the string "[object Object]"
true + true2each true becomes 1
1 < 2 < 3true1 < 2 is true, and true < 3 is 1 < 3
3 > 2 > 1false3 > 2 is true, and true > 1 is 1 > 1
"b" + "a" + +"a" + "a""baNaNa"+"a" is NaN, which joins as text
JavaScript
console.log([] + [], [] + {}, true + true);
console.log(1 < 2 < 3, 3 > 2 > 1);
console.log(("b" + "a" + +"a" + "a").toLowerCase());

None of these should appear in real code: they are what happens when types are mixed without thinking. Convert explicitly and compare with ===, and they never will.

Key points

  • String(x) converts anything to text; Number(x) converts the whole value or gives NaN.
  • parseInt(x, 10) and parseFloat(x) read a number from the start of a string and ignore the rest.
  • Eight values are falsy; "0", "false", [] and {} are truthy.
  • + joins when either side is a string; -, *, / convert to numbers.
  • Use ===; test for NaN with Number.isNaN; remember Number("") is 0.

Practice

  1. Convert "3.75" to a number three ways (Number, parseFloat, unary +) and add 1 to each.
  2. Predict, then check: "6" / "2", "6" + 2, "6" - "2", 6 + +"2".
  3. Write toNumberOrZero(text) that returns the number, or 0 when the text isn't a valid number.
  4. Sort ["10", "9", "100", "2"] as numbers, not as text.
Finished this lesson?Mark it complete to earn +20 XP and move your progress on.