
Use Number(chaine) to convert a string that must be entirely numeric (“42”, “3.99”): it returns NaN at the first stray character. Use parseInt(chaine, 10) or parseFloat(chaine) to extract the number at the start of a string such as “42px”. Always check the result with Number.isNaN() before doing any arithmetic.
A value read from a form field, an HTML attribute, a URL or a badly typed JSON file always arrives as a string. Before you can calculate with it, you have to convert it to a number. JavaScript offers four ways to do that, and they do not react the same way to “42px”, “3,99” or an empty string. Here is what each one really returns, checked under Node.js 22.
The four methods in one table
| String | parseInt(s, 10) | parseFloat(s) | Number(s) | +s |
|---|---|---|---|---|
"42" | 42 | 42 | 42 | 42 |
"3.99" | 3 | 3.99 | 3.99 | 3.99 |
"42px" | 42 | 42 | NaN | NaN |
" 42 " | 42 | 42 | 42 | 42 |
"" (empty) | NaN | NaN | 0 | 0 |
"12,5" | 12 | 12 | NaN | NaN |
"1e3" | 1 | 1000 | 1000 | 1000 |
"0x1f" | 0 (radix 10 forced) | 0 | 31 | 31 |
"abc" | NaN | NaN | NaN | NaN |
"Infinity" | NaN | Infinity | Infinity | Infinity |
null | NaN | NaN | 0 | 0 |
undefined | NaN | NaN | NaN | NaN |
Two families emerge. parseInt and parseFloat extract: they read the string from the start, stop at the first character that is not a digit and return what they have read so far. Number() and the + operator convert all or nothing: the whole string must represent a number, otherwise the result is NaN. One exception that stings: for them, the empty string is 0.
parseInt(): the integer at the start of the string
parseInt('42', 10); // 42
parseInt('42px', 10); // 42 : stops at the “p”
parseInt('3.99', 10); // 3 : the decimal part is dropped, not rounded
parseInt(' 42 ', 10); // 42 : surrounding spaces are tolerated
parseInt('abc', 10); // NaN : no digit at the start
parseInt('', 10); // NaNThe second argument is the radix (or base). It is 10 for a decimal number, 16 for hexadecimal, 2 for binary:
parseInt('ff', 16); // 255
parseInt('101', 2); // 5
parseInt('0x1f'); // 31 : without a radix, the 0x prefix switches to hexadecimal
parseInt('08'); // 8 : modern engines no longer read “08” as octalWriting the radix explicitly, parseInt(s, 10), has not been essential for leading zeros since ES5, but it is still the right habit: it makes the intention readable and neutralises the 0x case.
parseFloat(): the decimal number at the start of the string
parseFloat('3.99'); // 3.99
parseFloat('12.5px'); // 12.5
parseFloat('.5'); // 0.5
parseFloat('1e3'); // 1000 : scientific notation is understood
parseFloat('3,99'); // 3 : the comma stops the parsing
parseFloat('abc'); // NaNparseFloat has no radix argument: it always reads in decimal. It is the function to pick for extracting a measurement from a CSS string (“12.5px”, “1.5rem”) or from free text.
Number(): strict conversion
Number('42'); // 42
Number('3.99'); // 3.99
Number(' 42 '); // 42 : surrounding spaces and line breaks are ignored
Number('42px'); // NaN : one extra character and the whole thing fails
Number(''); // 0 : watch out
Number(' '); // 0 : same again
Number(null); // 0
Number(undefined); // NaN
Number(true); // 1
Number('0x1f'); // 31 : prefixed hexadecimal, binary (0b) and octal (0o) are acceptedNumber() is the conversion you want for a value that must be a number and nothing else: a price, a quantity, an identifier. If the user typed “12 euros”, you get NaN and you can tell them so, instead of unknowingly calculating with 12.
The unary + operator: Number() in a single character
+'42'; // 42
+''; // 0
+'abc'; // NaN
'42' * 1; // 42 : multiplication also forces the conversion
'42' - 0; // 42
'3' + '4'; // '34' : addition, on the other hand, concatenates!A + placed in front of a value applies exactly Number(). It is short, very common in existing code, and easy to miss in a code review: +valeur looks like a typo. In shared code, Number(valeur) says the same thing more clearly. And never use binary addition to convert: '3' + '4' gives the string '34'.
Handling NaN properly
NaN (Not a Number) is the result of any failed conversion. It has a treacherous property: it is equal to nothing, not even itself.
const n = Number('abc');
n === NaN; // false, always
Number.isNaN(n); // true : the right way to test
isNaN('abc'); // true : but isNaN converts its argument first...
Number.isNaN('abc');// false : ... whereas Number.isNaN only tests for genuine NaNThe usual pattern: convert, test, then apply a default value or report the error.
function toNumber(valeur, defaut = 0) {
const n = Number(String(valeur).trim());
return Number.isNaN(n) ? defaut : n;
}
toNumber('42'); // 42
toNumber('42px'); // 0 (default value)
toNumber('', 10); // 0 : the empty string is 0, not the default valueThe last line is a reminder of the empty-string trap: if an empty field must be treated as “not provided”, test for it before converting.
function toNumberStrict(valeur) {
const s = String(valeur).trim();
if (s === '') return NaN;
return Number(s);
}The French case: the decimal comma
None of the four methods understands “12,5”. Number returns NaN, and parseFloat stops at the comma and returns 12, which is worse: the program carries on with a wrong value. Normalise the input before converting.
function parseFr(chaine) {
const normalisee = String(chaine)
.replace(/\s/g, '') // removes the thousands spaces: “1 234,56”
.replace(',', '.'); // decimal comma -> point
return Number(normalisee);
}
parseFr('12,5'); // 12.5
parseFr('1 234,56'); // 1234.56
parseFr('12.5'); // 12.5 : the point is still acceptedIn a form, <input type="number"> avoids the problem: the browser shows the comma to a French user but always passes a point in value.
Checking that a string really is a number
To find out whether a string represents a number before using it, combine a strict conversion with Number.isFinite, which rejects both NaN and Infinity.
function estNumerique(chaine) {
const s = String(chaine).trim();
return s !== '' && Number.isFinite(Number(s));
}
estNumerique('42'); // true
estNumerique('3.99'); // true
estNumerique('42px'); // false
estNumerique(''); // false
estNumerique('Infinity'); // falseAnd to tell an integer from a decimal once converted: Number.isInteger(42) is true, Number.isInteger(3.99) is false.
Large integers: when Number is no longer enough
Number represents integers exactly up to 253 − 1 (9,007,199,254,740,991). Beyond that, precision is lost without warning: a 64-bit database identifier or a very large amount expressed in cents can come out wrong.
Number('9007199254740993'); // 9007199254740992 : the last digit has changed
BigInt('9007199254740993'); // 9007199254740993n : exact
Number.MAX_SAFE_INTEGER; // 9007199254740991BigInt cannot be mixed with Number in a calculation and does not accept decimals; keep it for identifiers and genuinely large integers.
Which method to choose?
- The value must be a number and nothing else (price, quantity, identifier):
Number(), after ruling out the empty string. - You need to extract a number from text (“12.5px”, “3 items”):
parseFloat()for a decimal,parseInt(s, 10)for an integer. - The input comes from a French user: normalise the comma and the spaces, then
Number(). - You are reading code that uses
+valeur: it isNumber(valeur). Keep it if the team is used to it, but never convert with the addition'3' + '4'. - In every case: test with
Number.isNaN()orNumber.isFinite()before calculating.
The reverse operation, turning a number into a readable string with two decimal places or a thousands separator, is covered in Formatting a number in JavaScript. For the basics of the language, the Variables and data types chapter of the tutorial explains why a string and a number are two distinct types.
Common errors
parseInt("08") is 8 in every modern engine, but parseInt("0x1f") is 31: the string is read as hexadecimal. Always pass the radix: parseInt(chaine, 10).Number("12,5") returns NaN and parseFloat("12,5") returns 12. Replace the comma with a point before converting.NaN === NaN is false. Use Number.isNaN(valeur), never a direct comparison.Number("") and +"" are 0, not NaN. A form field left empty therefore becomes a silent zero: test chaine.trim() === "" before converting.

