NaN — Detection & Avoiding Pitfalls
debt(d3/e1/b1/t7)
Closest to 'default linter catches the common case' (d3). ESLint has built-in rules that flag isNaN() usage and suggest Number.isNaN() as the replacement. The detection_hints explicitly list eslint as automated detection, making this a default-tooling catch for the most common mistake.
Closest to 'one-line patch or single-call swap' (e1). The quick_fix is explicit: 'Replace isNaN() with Number.isNaN().' This is a direct, mechanical substitution requiring no refactoring or cross-file changes. After parseInt/parseFloat, adding a check is similarly a single-line guard.
Closest to 'minimal commitment' (b1). NaN-checking is a localized validation concern. The choice to use Number.isNaN() over isNaN() imposes no structural debt—it's a local decision at the call site, affects only that validation point, and does not shape future architecture or require every maintainer to adopt a new pattern across the codebase.
Closest to 'serious trap' (t7). The canonical misconception is that isNaN() and Number.isNaN() are equivalent; isNaN() coerces its argument (e.g., isNaN('hello') returns true), contradicting how strict equality and numeric operations work elsewhere in JavaScript. Developers transitioning from other languages or unfamiliar with JavaScript's type coercion rules will naturally assume isNaN() is the correct check. Additionally, comparing NaN === NaN always returns false, which is non-obvious and contradicts equality semantics in most contexts.
TL;DR
Explanation
NaN (Not a Number) is produced by invalid arithmetic (0/0, parseInt('abc'), Math.sqrt(-1)). Its defining property: NaN !== NaN (the only JS value not equal to itself). isNaN('abc') returns true because isNaN() coerces to number first — isNaN converts 'abc' to NaN then checks. Number.isNaN('abc') returns false (correct — 'abc' is a string, not NaN). Always use Number.isNaN() for reliable detection. For any value that may not be a valid number: typeof n === 'number' && !Number.isNaN(n).
Common Misconception
Why It Matters
Common Mistakes
- Using isNaN() which returns true for strings.
- Not checking for NaN after parseInt/parseFloat on user input.
- Comparing with === NaN which always returns false.
Code Examples
isNaN('hello') // true — incorrect, 'hello' is a string, not NaN
isNaN(undefined) // true — coercion trap
NaN === NaN // false — cannot use ===
Number.isNaN(NaN) // true
Number.isNaN('hello') // false — correct
Number.isNaN(undefined) // false — correct
// Safe numeric check:
function isValidNumber(v) {
return typeof v === 'number' && !Number.isNaN(v) && isFinite(v);
}
// After parsing:
const price = parseFloat(input);
if (Number.isNaN(price)) throw new Error('Invalid price');