skip to content

How do JavaScript's global isNaN() and isFinite() differ from Number.isNaN() and Number.isFinite(), and which would you use to validate a numeric field?

level: middleimportance: must knowfreq 58%

answer

  1. two families, same-looking names
  2. one of them converts first
  3. isNaN("") is false
  4. non-numbers always fail the strict methods
  5. one check rejects NaN and Infinity together

basics

~10 s

The global isNaN and isFinite convert their argument to a number first, so isNaN("") is false and isFinite("100") is true. The ES2015 Number.isNaN and Number.isFinite never convert: any non-number returns false.

solid answer

~40 s

They are different functions that unfortunately share a name. The globals `isNaN` and `isFinite` apply numeric conversion to their argument before testing, so they answer "would this become NaN / a finite number if I converted it?" — hence `isNaN("")` is false because the empty string converts to `0`, `isNaN(null)` is false for the same reason, and `isFinite("100")` is true. The ES2015 methods `Number.isNaN` and `Number.isFinite` do no conversion at all: they return false for anything that is not already a Number, so `Number.isFinite("100")` is false. For validation I convert explicitly and then check: `const n = Number(raw); if (!Number.isFinite(n)) reject()`. That single guard rejects NaN, `Infinity` and `-Infinity` together. I also guard empty and whitespace-only strings separately, since `Number("")` is `0`, not NaN.

code

javascript · 13 lines
javascript
function parseAmount(raw) {
  if (typeof raw !== "string" || raw.trim() === "") return null;
  const n = Number(raw);
  return Number.isFinite(n) ? n : null;
}

console.log(parseAmount("12.5"));  // 12.5
console.log(parseAmount("12px"));  // null
console.log(parseAmount(""));      // null  (Number("") is 0 — guarded)
console.log(parseAmount("1e999")); // null  (overflows to Infinity)

console.log(isNaN(""), isFinite("100"));                 // false true
console.log(Number.isNaN(""), Number.isFinite("100"));   // false false

go deeper

for a junior

Remember there are two versions of each function and that the Number.* ones are the safe default. Be able to say Number.isNaN("abc") is false because it does not convert the argument.

for a middle

Explain the conversion step in the globals and give a concrete surprise from it, such as isNaN("") being false because the empty string converts to zero.

for a senior

Show where the guard belongs in a real pipeline: convert once at the boundary, reject non-finite values there, and explain why a downstream NaN check is too late to identify the offending field.

for a principal

Own the convention: which layer converts and validates, whether invalid input throws or is coerced to a default, and how that rule is enforced consistently so numeric junk never reaches storage or the UI.

## Two families with confusingly similar names JavaScript ships four functions here: - `isNaN(value)` and `isFinite(value)` — globals, present since ES1. - `Number.isNaN(value)` and `Number.isFinite(value)` — added in ES2015. The `Number.*` versions are not "namespaced aliases" of the globals. They implement a deliberately different, stricter contract, added precisely because the globals surprise people. ## What the globals actually do Each global first converts its argument to a Number (the same conversion `Number(value)` performs), then asks its question about the *converted* value. So `isNaN` really means "does this value become NaN when converted to a number?" and `isFinite` means "does this value become a finite number?". That conversion is where the surprises live: ```js isNaN(""); // false — Number("") is 0 isNaN(" "); // false — whitespace-only also converts to 0 isNaN(null); // false — Number(null) is 0 isNaN([]); // false — Number([]) is 0 isNaN([1, 2]); // true — Number([1,2]) is NaN isNaN(undefined); // true — Number(undefined) is NaN isNaN("12px"); // true isFinite("100"); // true isFinite(null); // true — because Number(null) is 0 ``` One further edge: converting a BigInt to a Number throws, so `isNaN(1n)` raises a `TypeError` rather than returning a boolean. The practical failure mode is using `isNaN` as a "is this text numeric?" test. It says the empty string, a whitespace string, `null` and `[]` are all fine numbers, which is almost never what a form validator wants. ## What the Number.* methods do `Number.isNaN(value)` returns `true` only when the argument is already the Number value NaN. `Number.isFinite(value)` returns `true` only when the argument is already a Number and is neither NaN nor `±Infinity`. Neither converts anything: ```js Number.isNaN("NaN"); // false — a string is not the number NaN Number.isNaN(undefined); // false Number.isNaN(0 / 0); // true Number.isFinite("100"); // false — a string is never finite Number.isFinite(null); // false Number.isFinite(42); // true Number.isFinite(Infinity); // false Number.isFinite(NaN); // false ``` A useful mental model: `Number.isFinite` is the closest thing JavaScript has to "is this a real, usable number?", because the Number type's three problem values — NaN, `Infinity`, `-Infinity` — are all excluded by one call, and non-numbers are excluded too. ## The validation pattern Because the strict methods do not convert, real validation is two explicit steps: convert deliberately, then check the result. ```js function parseAmount(raw) { if (typeof raw !== "string" || raw.trim() === "") return null; const n = Number(raw); return Number.isFinite(n) ? n : null; } ``` Three details matter in that function: 1. **The empty-string guard comes first.** `Number("")` and `Number(" ")` are `0`, not NaN, so without the guard a blank field silently becomes a zero amount — a classic bug that no NaN check will ever catch. 2. **`Number` versus `parseFloat`.** `Number("12px")` is NaN, while `parseFloat("12px")` is `12`: parseFloat stops at the first invalid character and returns the prefix it managed to read. For validation you usually want the all-or-nothing behaviour of `Number`. 3. **`Number.isFinite` also catches overflow.** `Number("1e999")` is `Infinity`, which is a Number and is not NaN — so a `Number.isNaN` check would let it through, and the value would later serialize or display as garbage. `Number.isFinite` rejects it. ## Choosing between them Use `Number.isNaN` when you specifically need "is this exact value the NaN number" — for example, inspecting the output of a computation you already know is numeric. Use `Number.isFinite` for input validation and for guarding arithmetic results. Reach for the globals essentially never; if you find one in a code review, the usual fix is an explicit `Number(...)` conversion followed by a strict check, which also makes the intended conversion visible to the next reader. A related trap when supplying fallbacks: NaN is falsy, so `value || 0` replaces NaN with `0` — but it also replaces a legitimate `0` and `""`. And `value ?? 0` does **not** replace NaN, because nullish coalescing only fires for `null` and `undefined`. The explicit `Number.isFinite(value) ? value : 0` says what you mean.

  • Why is Number.isFinite usually a better input guard than Number.isNaN?
    `Number.isNaN` only rejects NaN, so `Infinity` sails through — and `Number("1e999")` is `Infinity`, as is any overflowing computation. `Number.isFinite` rejects NaN, `Infinity` and `-Infinity` in one call, and rejects non-numbers too. If a value must be usable in arithmetic, storage or display, finite is the property you actually need.
  • Someone validates a form field with isNaN(input.value) and it lets blank submissions through. What is happening?
    The global `isNaN` converts first, and `Number("")` is `0` — a perfectly good number — so `isNaN("")` is false and the blank field passes. Whitespace-only strings behave the same way. The fix is an explicit empty check before conversion, then `Number.isFinite(Number(value))` on what remains.
  • How do Number("12px") and parseFloat("12px") differ, and which suits validation?
    `Number("12px")` is NaN: the whole string must be a valid numeric literal. `parseFloat("12px")` is `12`: it reads the longest valid prefix and ignores the rest. For validation `Number` is the safer choice, because parseFloat happily accepts `"12 apples"` and hands you a plausible-looking number derived from clearly invalid input.

saying these in an interview costs you the question

  • Believes Number.isNaN is just a namespaced alias of global isNaN
  • Uses isNaN as an "is this string numeric" validator
  • Forgets that Number("") is 0, so blank input passes as zero
  • Checks only for NaN and lets Infinity through validation
  • Expects Number.isFinite("100") to be true

context