NaN (Not a Number) in JavaScript — in plain words

Hi, I’m Aashish, a developer who documents his confusion so others can skip it. Most of what I write starts as a messy Notion page, but ends up as something other devs can actually use. Currently obsessed with integrating Typescript/React/Next with Laravel and making code readable enough that future me won’t file a bug report. Occasionally seen holding a guitar and pretending it’s therapy.
NaN shows up when JavaScript tries to make a number, but it can’t. You’ll see it a lot while parsing strings or doing invalid math. Fun fact: typeof NaN is "number".
A quick look with parseInt
Here foo looks like a number, but its type is string. Using parseInt turns it into a real number.
let foo = "1";
console.log(parseInt(foo)); // 1
console.log(typeof parseInt(foo)); // "number"
Now, what if foo is not a number string but any other string? parseInt can’t make foo number, so it returns NaN.
let foo = "Momo";
console.log(parseInt(foo)); // NaN
console.log(typeof parseInt(foo)); // "number"
NaN is kind of unique
In JS, NaN is never equal to anything, not even itself. So we cannot use === to check NaN.
console.log(NaN === NaN); // false
console.log(NaN == NaN); // false
To check for NaN, use Number.isNaN(value). isNaN(value) also works, but it coerces values first.
let foo = 'Momo';
isNaN(foo); // true (coerces foo (which is a string) to -> NaN)
Number.isNaN(foo); // false (foo is not the NaN value but is a string)
Note: Object.is(NaN, NaN) is also true. It’s another safe way to compare with NaN.
Object.is(NaN, NaN); // true
More ways NaN appears
It’s not only about parsing. Any invalid math can cause NaN.
0 / 0; // NaN
Math.sqrt(-1); // NaN
Infinity - Infinity; // NaN
"abc" * 2; // NaN
"10" - "a"; // NaN
Number(undefined); // NaN
parseInt vs Number vs parseFloat
parseInt stops at the first non‑digit. Number needs the whole string to be valid.
parseInt("3.14px", 10); // 3
parseFloat("3.14px"); // 3.14
Number("3.14px"); // NaN
Practical checks you will use
Check for a real, usable number. This avoids NaN and Infinity.
function isRealNumber(foo) {
return typeof foo === "number" && Number.isFinite(foo);
}
Safely turn user input into a number. Return null if it’s not valid.
function toNumberOrNull(input) {
if (String(input).trim() === "") return null;
const n = Number(input);
return Number.isNaN(n) ? null : n;
}
JSON note
JSON does not support NaN. It becomes null when you stringify.
JSON.stringify({ a: NaN }); // '{"a":null}'
Tiny cheat sheet
typeof NaN==="number"NaN===NaNisfalseUse
Number.isNaN(x)for checksisNaN(x)coerces, be carefulComparisons with
NaNare alwaysfalse
Final note
Anything
===NaNwill always return false. Also,typeof NaNis "number". Keep these in mind, andNaNstops being scary.