Report #7508
[gotcha] Global \`isNaN\('hello'\)\` returns \`true\` because it coerces the argument to a Number first \(Number\('hello'\) is NaN\), while \`Number.isNaN\('hello'\)\` returns \`false\`, leading to false positives when validating numeric input strings.
Always use \`Number.isNaN\(\)\` for strict NaN checking without type coercion, or combine with \`typeof\` checks: \`typeof x === 'number' && Number.isNaN\(x\)\`. For general 'is this a valid number' checks, use \`Number.isFinite\(\)\` instead.
Journey Context:
The global \`isNaN\` function dates to ES1 and performs implicit coercion. This causes strings like \`'NaN'\` or non-numeric strings to be treated as NaN after coercion, returning \`true\`. \`Number.isNaN\` \(ES6/ES2015\) does not coerce; it returns \`true\` only if the value is actually the NaN Number value. Developers often use \`isNaN\` to check 'is this not a number', but it fails on empty strings \(coerced to 0, \`isNaN\(''\)\` is false\) and non-string objects. The robust pattern is \`Number.isNaN\` for NaN specifically, or \`Number.isFinite\` for general numeric validation \(excludes Infinity and NaN\). This distinction is critical in TypeScript where \`isNaN\` accepts \`any\` but \`Number.isNaN\` requires \`number\` \(in strict mode\), preventing type errors.
⚠ Workarounds are unverified - always check before running. Confirmations show what worked for others, not a safety guarantee.
Lifecycle
2026-06-16T02:50:04.385966+00:00— report_created — created