I’m working on a small JavaScript project and I’m stuck converting user input from a string to a number. Some values work, others break my calculations or return NaN. What’s the reliable way to convert strings to numbers in JavaScript, and are there any common pitfalls I should watch out for?
The “reliable way” depends on what kind of number you expect and how strict you want to be.
- Quick win for normal numbers
If the string is a plain number:
‘42’
‘3.14’
‘-7’
Use Number:
const n = Number(input)
It returns a number or NaN.
Number(‘42’) // 42
Number(‘3.14’) // 3.14
Number(’ 10 ‘) // 10
Number(‘10px’) // NaN
Number(’‘) // 0
Number(’ ') // 0
Number(‘foo’) // NaN
The empty string turning into 0 often bites people. So you should check input.length first.
- Integers only
Use parseInt, but always pass a radix:
const n = parseInt(input, 10)
Examples:
parseInt(‘10’, 10) // 10
parseInt(‘10.5’, 10) // 10
parseInt(‘010’, 10) // 10
parseInt(‘10px’, 10) // 10
parseInt(‘px10’, 10) // NaN
parseInt stops at the first non digit. This is good for stuff like ‘10px’, bad if you expect the whole string to be numeric.
- Floats
Use parseFloat:
const n = parseFloat(input)
parseFloat(‘3.14’) // 3.14
parseFloat(‘3.14px’) // 3.14
parseFloat(‘px3.14’) // NaN
Same issue. It accepts partial numbers.
- Fast shorthand
The unary plus turns a string into a number:
const n = +input
Examples:
+‘42’ // 42
+‘3.14’ // 3.14
+‘10px’ // NaN
+‘’ // 0
It behaves like Number(input). Use it when you are comfortable with that behavior and you want short code.
- Common NaN traps
a) Empty inputs
const input = prompt(‘Enter number’)
const n = Number(input)
If user hits Cancel, input is null. Number(null) is 0. If user enters ‘’, Number(‘’) is 0. That often breaks logic.
Better:
if (input == null || input.trim() === ‘’) {
// handle missing value
} else {
const n = Number(input)
if (Number.isNaN(n)) {
// handle invalid number
}
}
b) Whitespace and commas
Number(’ 123 ') // 123
Number(‘1,234’) // NaN
If your users type “1,234.56”, you need to sanitize first:
const cleaned = input.replace(/,/g, ‘’)
const n = Number(cleaned)
c) Boolean-like strings
Number(‘true’) // NaN
Number(‘false’) // NaN
You need custom logic:
const n = input === ‘true’ ? 1 :
input === ‘false’ ? 0 :
Number(input)
- Strict validation pattern
If you want to fail when the whole thing is not a pure number string, combine regex with Number:
function toNumberStrict(str) {
if (typeof str !== ‘string’) return NaN
const trimmed = str.trim()
if (!/^-?\d+(.\d+)?$/.test(trimmed)) return NaN
return Number(trimmed)
}
toNumberStrict(‘42’) // 42
toNumberStrict(‘42a’) // NaN
toNumberStrict(‘3.14’) // 3.14
toNumberStrict(’ 3.14 ') // 3.14
- Which one should you use
Most forms or prompts:
const n = Number(input)
if (Number.isNaN(n)) {
// tell user input is invalid
}
If you expect integers only:
const n = Number(input)
if (!Number.isInteger(n)) {
// invalid or not an integer
}
If you accept “10px” style values:
const n = parseInt(input, 10)
if (Number.isNaN(n)) {
// invalid
}
- Debugging why your calc breaks
Log both the value and typeof:
console.log(‘input:’, input, ‘type:’, typeof input)
const n = Number(input)
console.log(‘n:’, n, ‘isNaN:’, Number.isNaN(n))
If something turns into NaN, walk back from there:
• Check for empty string, null, undefined
• Check for commas or text like ‘10px’
• Check if trimming fixes it
Once you handle empty input and check Number.isNaN on every conversion, your calculations stop blowing up.
If your calcs are randomly exploding into NaN, the main thing you’re missing is usually validation logic, not just “which function do I call.”
@shizuka covered the basic tools really well (Number, parseInt, parseFloat, unary +). I’d actually say the reliable way is: pick one conversion method, then wrap it in your own small helper that enforces your rules.
Something like:
function toNumberStrict(input) {
if (input == null) return NaN // null or undefined
if (typeof input !== 'string') {
// optional: allow numbers to pass through
if (typeof input === 'number') return input
return NaN
}
const trimmed = input.trim()
if (trimmed === ') return NaN // don't silently treat empty as 0
const n = Number(trimmed)
return Number.isFinite(n) ? n : NaN
}
Usage:
const raw = prompt('Enter a number:')
const value = toNumberStrict(raw)
if (Number.isNaN(value)) {
alert('That is not a valid number')
} else {
console.log('OK, value:', value)
}
Key differences from some of what was suggested:
- I avoid relying on
Number(') === 0orNumber(null) === 0. That behavior is technically correct but practically terrible for user input, because “no input” is not the same as zero. - I don’t use
parseInt/parseFloatfor general user input, because'10px'turning into10can hide bugs. Great for CSS parsing, bad for “user typed a number in a form.” - I insist on
Number.isFinite, not just!Number.isNaN, so junk like'Infinity'does not silently pass as a “valid” number.
If you do need to support stuff like 1,234.56 from users, extend the helper:
function toNumberLocaleish(input) {
if (input == null) return NaN
const trimmed = String(input).trim()
if (trimmed === ') return NaN
// naive: strip commas
const cleaned = trimmed.replace(/,/g, ')
const n = Number(cleaned)
return Number.isFinite(n) ? n : NaN
}
Then your workflow becomes:
- Read raw input.
- Convert with one helper function.
- Immediately check
Number.isNaN(result)or maybe alsoNumber.isFinite(result). - Only after that do any calculations.
If you follow that pattern, you basically stop seeing “random” NaNs: they all get caught at the conversion step instead of leaking into the rest of your code and blowing up 5 lines later.