- 業務・実務で使える JavaScript ユーティリティ:NaNを安全に判定する方法
- まず整理する:NaNとは何か
- ステップ1:なぜ NaN === NaN で判定できないのか理解する
- ステップ2:Number.isNaN() を使う
- ステップ3:判定したい対象を分けて考える
- ステップ4:NaNだけでなく Infinity も一緒に考える
- ステップ5:どの段階で判定するかを決める
- 業務で使える基本テンプレート その1:NaNそのものを厳密に判定する
- 業務で使える基本テンプレート その2:数値変換の失敗を判定する
- 業務で使える完成版テンプレート:安全に数値へ変換する
- 初心者が間違えやすいポイントを深掘りする
- 用途別の判定例
- テストで境界条件を確認する
- まとめ
業務・実務で使える JavaScript ユーティリティ:NaNを安全に判定する方法
JavaScriptで計算や数値変換をしていると、ある日突然 NaN が出てきて困ることがあります。フォーム入力の計算、CSVの読み込み、APIレスポンスの加工、割引率の計算、ページ番号の変換など、実務では珍しくありません。
ただし、NaN を安全に判定するには、最初に知っておくべき重要な特徴があります。NaN は「数値ではない」という意味の名前ですが、JavaScriptの型としては number です。さらに、NaN === NaN が false になるという、初心者がつまずきやすい性質もあります。
この記事では、NaN の意味から始めて、なぜ普通の比較で判定できないのか、どの関数を使うべきか、実務で再利用しやすいユーティリティをどう設計するかを、段階的に説明します。
まず整理する:NaNとは何か
NaNは「数値として成立しない結果」を表す特殊な値
NaN は Not-a-Number の略です。数値として計算しようとしたが、正しい数値結果にならなかったときに現れます。
// 数値に変換できない文字列を Number() へ渡す
console.log(Number("abc")); // NaN
// 数学的に定義できない計算の一部でも NaN が出る
console.log(0 / 0); // NaNJavaScript業務では、利用者が入力した値が数値でなかったり、計算の途中で不正な値が混ざったりすると、NaN が発生することがあります。
NaNは number 型である
ここが最初の重要ポイントです。名前に「Not-a-Number」と入っていますが、JavaScriptでは NaN は数値型です。
// NaN の型は number
console.log(typeof NaN); // "number"JavaScriptそのため、typeof value === "number" だけでは、正常な数値と NaN を区別できません。型判定と、値として妥当かどうかの判定は別に考える必要があります。
ステップ1:なぜ NaN === NaN で判定できないのか理解する
NaN のもう1つの大きな特徴は、自分自身と等しくないことです。
// NaN は、自分自身と比較しても false になる
console.log(NaN === NaN); // false
JavaScriptこの仕様のため、次のような判定は使えません。
// この判定は期待どおりに動かない
const value = Number("abc");
console.log(value === NaN); // falseJavaScriptつまり、=== や == で NaN を判定しようとしてはいけません。これは初心者が最初につまずきやすいポイントです。
逆に、この性質を利用した判定もある
NaN だけが自分自身と等しくない、という性質を使って、value !== value で判定する方法もあります。
// NaN だけが自分自身と一致しない
function isNaNBySelfComparison(value) {
return value !== value;
}
console.log(isNaNBySelfComparison(NaN)); // true
console.log(isNaNBySelfComparison(123)); // false
console.log(isNaNBySelfComparison("abc")); // falseJavaScriptただし、この書き方は仕組みを知っていないと読みづらいため、実務コードでは通常、専用のAPIを使うほうがわかりやすいです。仕組みの理解としては役立ちますが、チーム開発で標準にする必要はありません。
ステップ2:Number.isNaN() を使う
NaNそのものだけを厳密に判定する
実務でまず覚えたいのは Number.isNaN() です。この関数は、値が本当に NaN のときだけ true を返します。
// NaN だけを厳密に判定する
console.log(Number.isNaN(NaN)); // true
console.log(Number.isNaN(Number("abc"))); // true
console.log(Number.isNaN(123)); // false
console.log(Number.isNaN("abc")); // false
console.log(Number.isNaN(undefined)); // falseJavaScriptここで重要なのは、"abc" そのものは NaN ではないということです。文字列は文字列であり、数値変換した結果として NaN になるかどうかは別の話です。
グローバルの isNaN() との違い
似た名前の isNaN() というグローバル関数もありますが、こちらは引数をいったん数値へ変換してから判定します。そのため、初心者向けの実務コードでは意図しない true が出やすいです。
// グローバルの isNaN() は、自動で数値変換してから判定する
console.log(isNaN("abc")); // true
console.log(isNaN(undefined)); // true
console.log(isNaN({})); // true
// 空文字や空白だけは 0 として扱われるため false になる
console.log(isNaN("")); // false
console.log(isNaN(" ")); // false
// Number.isNaN() は NaN そのものだけを見る
console.log(Number.isNaN("abc")); // false
console.log(Number.isNaN(undefined)); // false
console.log(Number.isNaN({})); // falseJavaScriptこの違いを理解していないと、「文字列かどうか」を見たいのか、「数値変換した結果が壊れているか」を見たいのかが曖昧になります。実務では、特別な理由がない限り Number.isNaN() を優先するほうが読みやすく、安全です。
ステップ3:判定したい対象を分けて考える
NaN 判定の実務で大切なのは、「何を判定したいのか」を先に整理することです。主に次の2パターンがあります。
- すでに計算した結果や変換後の値が、
NaNになっていないか確認したい。 - 入力文字列を数値へ変換したときに、失敗するか確認したい。
この2つは似ていますが、コードの責務が異なります。
変換後の結果を検査する例
// 変換や計算の後で NaN かを確認する
const value = Number("abc");
if (Number.isNaN(value)) {
console.log("数値変換に失敗しました");
}JavaScriptこの用途では、Number.isNaN() がそのまま使えます。
入力文字列が数値として不正かを確認する例
// 入力文字列の妥当性を確認する
function isInvalidNumberInput(value) {
// 文字列以外はこの関数では不正とみなす
if (typeof value !== "string") {
return true;
}
// 前後の空白を除去する
const trimmed = value.trim();
// 空文字は未入力として不正にする
if (trimmed === "") {
return true;
}
// 数値変換の結果が NaN なら不正
return Number.isNaN(Number(trimmed));
}
console.log(isInvalidNumberInput("123")); // false
console.log(isInvalidNumberInput("abc")); // true
console.log(isInvalidNumberInput(" ")); // trueJavaScriptこの例では、「値そのものが NaN か」ではなく、「数値として使える入力か」を判定しています。役割を分けて書くことで、コードの意図が伝わりやすくなります。
ステップ4:NaNだけでなく Infinity も一緒に考える
実務では、NaN だけを弾けば十分とは限りません。Infinity や -Infinity も、通常の金額や件数では不正として扱いたいことが多いです。
// Infinity は NaN ではない
console.log(Number.isNaN(Infinity)); // false
console.log(Number.isNaN(-Infinity)); // false
JavaScriptそのため、「NaNではない」ことを確認するだけでは、通常の数値として安全とは言い切れません。業務では、NaNチェックよりも、有限の数値かどうかを確認する Number.isFinite() のほうが実用的な場面が多いです。
// 有限の通常数だけを true にする
console.log(Number.isFinite(123)); // true
console.log(Number.isFinite(3.14)); // true
console.log(Number.isFinite(NaN)); // false
console.log(Number.isFinite(Infinity)); // false
console.log(Number.isFinite("123")); // false
JavaScriptつまり、NaN を安全に判定したいという課題の先には、「この値を計算や業務処理へ渡してよいか」という目的があります。その視点で考えると、Number.isNaN() と Number.isFinite() を使い分けることが大切です。
ステップ5:どの段階で判定するかを決める
実務コードでは、NaNを見つけた時点で場当たり的に処理するより、入力から変換までの流れをそろえるほうが保守しやすくなります。考え方としては、次の順番がわかりやすいです。
- 入力の型を確認する。
- 必要なら前処理を行う。
- 数値へ変換する。
NaNやInfinityを除外する。- 整数、範囲、0以上などの業務ルールを確認する。
この順番を関数としてまとめておくと、画面入力、CSV、APIなど複数の経路で同じルールを使いやすくなります。
業務で使える基本テンプレート その1:NaNそのものを厳密に判定する
まずは、値が本当に NaN かだけを確認する、最小の関数です。
/**
* 値が NaN かどうかを厳密に判定します。
*
* @param {unknown} value 判定対象
* @returns {boolean} NaN なら true
*/
function isActualNaN(value) {
// Number.isNaN() は NaN そのものだけを true にする
return Number.isNaN(value);
}
console.log(isActualNaN(NaN)); // true
console.log(isActualNaN(Number("abc"))); // true
console.log(isActualNaN("abc")); // false
console.log(isActualNaN(undefined)); // falseJavaScriptこの関数は、計算結果や変換後の値の検査に使いやすいです。一方で、入力文字列を直接渡して判定する用途には向いていません。
業務で使える基本テンプレート その2:数値変換の失敗を判定する
次に、フォーム入力などの文字列を受け取り、数値変換に失敗したかを判定する関数です。
/**
* 文字列入力が数値変換に失敗するかを判定します。
*
* 仕様:
* - 文字列以外は不正とみなします。
* - 前後の空白を除去します。
* - 空文字は未入力として不正にします。
* - Number() の結果が NaN なら true を返します。
*
* @param {unknown} value 判定対象
* @returns {boolean} 数値変換に失敗するなら true
*/
function isNaNInput(value) {
// 文字列以外は、この関数では不正とする
if (typeof value !== "string") {
return true;
}
// 前後の空白を除去する
const trimmed = value.trim();
// 空文字は変換前に不正とする
if (trimmed === "") {
return true;
}
// 数値変換の結果が NaN なら失敗
return Number.isNaN(Number(trimmed));
}
console.log(isNaNInput("123")); // false
console.log(isNaNInput("3.14")); // false
console.log(isNaNInput("abc")); // true
console.log(isNaNInput("")); // true
console.log(isNaNInput(" ")); // trueJavaScriptただし、この関数は「NaNになるか」だけを見ています。Infinity は NaN ではないため、必要に応じてさらに条件を追加する必要があります。
業務で使える完成版テンプレート:安全に数値へ変換する
実務では、最終的に知りたいことは「NaNかどうか」そのものより、「この値を通常の数値として使ってよいか」であることが多いです。そこで、変換と判定をまとめた関数を用意します。
/**
* 入力値を安全に有限数へ変換します。
*
* 仕様:
* - 数値型はそのまま確認します。
* - 文字列は trim() 後に Number() で変換します。
* - 空文字、空白だけの文字列は不正です。
* - NaN、Infinity、-Infinity は不正です。
* - 変換できたら number を返し、失敗したら null を返します。
*
* @param {unknown} value 入力値
* @returns {number | null} 正常な有限数なら number、失敗なら null
*/
function toFiniteNumber(value) {
// すでに数値型なら、そのまま有限か確認する
if (typeof value === "number") {
return Number.isFinite(value) ? value : null;
}
// 文字列なら前処理してから変換する
if (typeof value === "string") {
const trimmed = value.trim();
// 空文字は未入力として扱う
if (trimmed === "") {
return null;
}
const converted = Number(trimmed);
// NaN や Infinity を除外する
return Number.isFinite(converted) ? converted : null;
}
// それ以外の型は不正とする
return null;
}
/**
* 値が安全に使える数値かどうかを判定します。
*
* @param {unknown} value 入力値
* @returns {boolean} 有限数として使えるなら true
*/
function isUsableNumber(value) {
return toFiniteNumber(value) !== null;
}
// 利用例
console.log(toFiniteNumber("123")); // 123
console.log(toFiniteNumber(" 3.14 ")); // 3.14
console.log(toFiniteNumber("abc")); // null
console.log(toFiniteNumber("")); // null
console.log(toFiniteNumber(NaN)); // null
console.log(toFiniteNumber(Infinity)); // null
console.log(isUsableNumber("123")); // true
console.log(isUsableNumber("abc")); // false
console.log(isUsableNumber(Infinity)); // falseJavaScriptこの関数の利点は、NaN だけでなく、空文字や Infinity もまとめて扱えることです。業務ルールとして「使える数値」を定義しやすくなります。
初心者が間違えやすいポイントを深掘りする
isNaN() は「NaNそのもの」を見る関数ではない
グローバルの isNaN() は、名前だけ見ると便利そうですが、実際には「数値へ変換した結果が NaN か」を見ています。そのため、入力検証では予想外の結果になります。
// 自動変換の影響で結果がわかりにくい
console.log(isNaN("abc")); // true
console.log(isNaN("123")); // false
console.log(isNaN("")); // false
console.log(isNaN(" ")); // false
console.log(isNaN(true)); // false
console.log(isNaN(undefined)); // true
JavaScriptたとえば空文字が false になるのは、数値変換で 0 になるためです。未入力を見逃しやすくなるため、実務では注意が必要です。
NaN チェックだけでは通常の数値を保証できない
Number.isNaN(value) が false でも、その値が通常の数値とは限りません。たとえば Infinity は NaN ではありません。
// NaN ではなくても、業務で使いたくない値がある
console.log(Number.isNaN(Infinity)); // false
console.log(Number.isNaN(-Infinity)); // false
JavaScript金額、個数、割合などを扱うなら、NaNかどうかよりも、有限の通常数かどうかで考えるほうが安全です。
空文字や空白だけは Number() で 0 になる
数値変換の挙動で、特に実務事故につながりやすいのが空文字です。
// 空文字や空白だけは 0 になる
console.log(Number("")); // 0
console.log(Number(" ")); // 0
JavaScript未入力なのに 0 として集計されると、売上や件数のデータがおかしくなります。そのため、入力文字列を扱うときは、まず trim() して空かどうかを確認する癖を付けるとよいです。
parseInt() や parseFloat() の結果で NaN 判定すると、部分一致を見逃す
parseInt() や parseFloat() は、先頭から読める部分だけを数値として扱います。そのため、入力全体が正しい数値かを確認したい用途には向きません。
// 途中まで読めれば数値扱いされる
console.log(parseInt("123abc", 10)); // 123
console.log(parseFloat("3.14xyz")); // 3.14
JavaScriptこの結果に対して Number.isNaN() を使っても、入力全体の不正は見つけられません。厳密な入力検証では、Number() と空文字チェックを組み合わせるほうが扱いやすくなります。
用途別の判定例
ページ番号:1以上の整数であることを確認する
/**
* ページ番号として妥当か判定します。
*
* @param {unknown} value 入力値
* @returns {boolean} 1以上の整数なら true
*/
function isPageNumber(value) {
const number = toFiniteNumber(value);
if (number === null) {
return false;
}
return Number.isInteger(number) && number >= 1;
}
console.log(isPageNumber("1")); // true
console.log(isPageNumber("0")); // false
console.log(isPageNumber("abc")); // false
console.log(isPageNumber("1.5")); // falseJavaScript金額:0以上の有限数であることを確認する
/**
* 金額として妥当か判定します。
*
* @param {unknown} value 入力値
* @returns {boolean} 0以上の有限数なら true
*/
function isPrice(value) {
const number = toFiniteNumber(value);
if (number === null) {
return false;
}
return number >= 0;
}
console.log(isPrice("1000")); // true
console.log(isPrice("3.14")); // true
console.log(isPrice("-1")); // false
console.log(isPrice("abc")); // falseJavaScript除算結果のチェック:計算後に NaN を検査する
/**
* 2つの値を割り算し、安全な数値で返します。
*
* @param {unknown} a 割られる値
* @param {unknown} b 割る値
* @returns {number | null} 正常なら結果、失敗なら null
*/
function safeDivide(a, b) {
const left = toFiniteNumber(a);
const right = toFiniteNumber(b);
// 入力の段階で不正な値を除外する
if (left === null || right === null) {
return null;
}
const result = left / right;
// 0除算や想定外の値を除外する
return Number.isFinite(result) ? result : null;
}
console.log(safeDivide("10", "2")); // 5
console.log(safeDivide("10", "0")); // null
console.log(safeDivide("abc", "2")); // nullJavaScriptこのように、入力変換と計算結果の検査を分けておくと、どこで不正値が混ざったかを追いやすくなります。
テストで境界条件を確認する
NaN判定は短いコードでも、境界条件が多いです。通常の数値だけでなく、空文字、空白、Infinity、文字列混在なども確認します。
// toFiniteNumber() のテスト例
const testCases = [
[123, 123],
[3.14, 3.14],
[NaN, null],
[Infinity, null],
[-Infinity, null],
["123", 123],
[" 3.14 ", 3.14],
["abc", null],
["", null],
[" ", null],
["123abc", null],
[null, null],
[undefined, null],
[true, null],
[false, null],
[{}, null]
];
// 期待値と実際の値を比較する
for (const [input, expected] of testCases) {
const actual = toFiniteNumber(input);
console.log({
input,
expected,
actual,
pass: Object.is(actual, expected)
});
}JavaScriptまた、Number.isNaN() と isNaN() の違いを理解するために、比較テストを書いておくのも有効です。
// 2つの関数の違いを確認する
const values = [NaN, "abc", "", " ", undefined, 123];
for (const value of values) {
console.log({
value,
globalIsNaN: isNaN(value),
numberIsNaN: Number.isNaN(value)
});
}JavaScriptこのようなテストは、チーム内で「どの関数を標準にするか」を共有するときにも役立ちます。
まとめ
JavaScriptでNaNを安全に判定するには、まず NaN の性質を正しく理解することが大切です。NaN は数値型であり、通常の比較演算では判定できません。
NaN === NaNはfalseになるため、比較演算で判定しません。NaNそのものを厳密に判定するなら、Number.isNaN()を使います。- グローバルの
isNaN()は自動変換を行うため、入力検証では注意が必要です。 - 実務では、
NaNだけでなくInfinityも除外するため、Number.isFinite()を使う場面が多くなります。 - 空文字や空白は
Number()で 0 になるため、変換前のチェックが重要です。 - 入力の正規化、数値変換、業務ルールの判定を分けると保守しやすくなります。
実務で本当に必要なのは、「NaNを見つけること」そのものではなく、「不正な入力や壊れた計算結果を業務処理へ流さないこと」です。用途を限定した小さな関数と、明確な入力ルールを組み合わせることで、安心して再利用できるコードになります。
