Contents
  1. Where NaN Comes From
  2. The First Problem: typeof NaN Returns “number”
  3. The Second Problem: NaN Is Not Equal to Itself
  4. The Third Problem: the Global isNaN Function
  5. The Correct Detection Method: Number.isNaN
  6. The Fourth Problem: NaN Propagates Silently
  7. What to Do Now
← All posts

Awful Parts in JS: NaN

NaN stands for Not a Number, yet typeof NaN returns 'number', and NaN !== NaN. Here is what makes it confusing and how to handle it correctly.

NaN stands for Not a Number. It is the value returned when a mathematical operation produces a result that cannot be represented as a number. It is one of the more confusing values in JavaScript because its behaviour contradicts what its name implies and because the tools for detecting it have historically been unreliable.

Where NaN Comes From

NaN is produced when a numeric operation fails to produce a valid result:

0 / 0              // NaN
parseInt('hello')  // NaN
Math.sqrt(-1)      // NaN
undefined + 1      // NaN

These operations do not throw errors. They return NaN and allow execution to continue. The intent is that programs can handle numeric failure without crashing.

The First Problem: typeof NaN Returns “number”

NaN is of type number. This is not a JavaScript-specific quirk. It follows from the IEEE 754 floating-point standard, which JavaScript uses for all numeric values. NaN is defined within the number type as a sentinel value representing an undefined or unrepresentable result.

typeof NaN; // 'number'

The practical consequence is that typeof cannot be used to detect NaN. A check for the type 'number' will return true for both valid numbers and NaN.

The Second Problem: NaN Is Not Equal to Itself

NaN is the only value in JavaScript that is not equal to itself. Any comparison involving NaN returns false, including equality with itself.

NaN === NaN; // false
NaN == NaN;  // false
NaN > 1;     // false
NaN < 1;     // false
NaN !== NaN; // true

This means the standard approach of checking a value by comparing it to a known value does not work with NaN.

The Third Problem: the Global isNaN Function

The global isNaN() function was designed to check for NaN, but it coerces its argument to a number before testing. This produces false positives:

isNaN('hello');    // true  — 'hello' coerces to NaN
isNaN(undefined);  // true  — undefined coerces to NaN
isNaN(null);       // false — null coerces to 0
isNaN('');         // false — '' coerces to 0

The function does not answer the question “is this value NaN.” It answers the question “does this value coerce to NaN,” which is a different and less useful question.

The Correct Detection Method: Number.isNaN

ES6 introduced Number.isNaN(), which does not coerce its argument. It returns true only when the value is actually NaN, not when the value merely converts to NaN.

Number.isNaN(NaN);       // true
Number.isNaN('hello');   // false
Number.isNaN(undefined); // false
Number.isNaN(42);        // false

Number.isNaN is the correct tool for detecting NaN. The global isNaN should be avoided.

The Fourth Problem: NaN Propagates Silently

Any computation that involves NaN produces NaN. If NaN enters a chain of calculations, it contaminates every result downstream without producing an error.

const a = parseInt('bad input'); // NaN
const b = a + 100;               // NaN
const c = b * 2;                 // NaN

console.log(c); // NaN

The original bad input may be far removed from where the NaN eventually surfaces, making the source of the problem difficult to trace.

What to Do Now

Test the difference between isNaN and Number.isNaN directly:

console.log(isNaN('100'));        // false — coerces to 100
console.log(isNaN('abc'));        // true  — coerces to NaN
console.log(isNaN(undefined));    // true  — coerces to NaN

console.log(Number.isNaN('abc')); // false — not NaN, it's a string
console.log(Number.isNaN(NaN));   // true
console.log(Number.isNaN(0/0));   // true

Validate numeric inputs at the boundary where they enter your application. Catching NaN early, at the point of input or conversion, prevents it from propagating through calculations where it becomes much harder to find.

← All posts