JavaScript Tips | 基本・共通ユーティリティ:型チェック – NaN 判定

JavaScript JavaScript
スポンサーリンク

NaN 判定とは何を見分けたいのか

NaN 判定は、「その値が“数値型ではあるけれど壊れた値(NaN)かどうか”」を見分けることです。
NaN は “Not a Number” の略ですが、型としては number です。ここがまずややこしいポイントです。

console.log(typeof NaN); // "number"
JavaScript

業務コードでは、計算の途中で NaN が紛れ込むと、その後の計算結果がすべて NaN になり、
しかもエラーにならず静かに壊れていくので、バグの原因としてかなり厄介な存在です。
だからこそ、「NaN かどうかを正しく判定するユーティリティ」がとても重要になります。


なぜ NaN はそんなに厄介なのか

自分自身とすら等しくない

NaN の一番のクセは、「自分自身とすら等しくない」という性質です。

console.log(NaN === NaN); // false
JavaScript

普通の値なら x === x は必ず true ですが、NaN だけは false になります。
そのため、「value === NaN で判定しよう」とすると、絶対にうまくいきません。

const value = NaN;

if (value === NaN) {
  console.log("NaN です"); // ここには絶対来ない
}
JavaScript

=== NaN では判定できない」というのは、NaN 判定の最初の重要ポイントです。

計算に混ざると全部 NaN になる

NaN が一度計算に混ざると、その後の結果も全部 NaN になります。

const a = NaN;
const b = a + 10;
const c = b * 2;

console.log(a); // NaN
console.log(b); // NaN
console.log(c); // NaN
JavaScript

しかも、エラーにはならず、ただ NaN が伝染していくだけなので、
「どこで NaN が発生したのか」を後から追いかけるのがとても大変です。

だからこそ、「怪しいところで NaN かどうかをチェックする」「NaN を見つけたら早めに止める」という設計が大事になります。


正しい NaN 判定:Number.isNaN

NaN かどうかを判定する正しい方法は、Number.isNaN を使うことです。

console.log(Number.isNaN(NaN));        // true
console.log(Number.isNaN(10));         // false
console.log(Number.isNaN("abc"));      // false
console.log(Number.isNaN("10"));       // false
console.log(Number.isNaN(Number("abc"))); // true
JavaScript

ここでのポイントは、「Number("abc") のように“数値変換に失敗した結果の NaN”を検出できる」ということです。
一方で、単なる文字列 "abc" に対しては false になります。「NaN という値そのもの」を判定しているイメージです。

isNaN(グローバル関数)との違い

歴史的に、Number.isNaN とは別に、グローバル関数 isNaN も存在します。

console.log(isNaN(NaN));     // true
console.log(isNaN("abc"));   // true(ここがややこしい)
console.log(isNaN("10"));    // false
JavaScript

isNaN は、「一度 Number に変換してから NaN かどうかを見る」関数なので、
"abc" のような文字列も「数値に変換すると NaN になるから true」と判定してしまいます。

実務では、この挙動が紛らわしくバグの元になるので、
「NaN 判定には Number.isNaN を使う」と覚えてしまうのがおすすめです。


業務ユーティリティとしての isNaNStrict

プロジェクト内で「NaN 判定は必ずこれを通す」と決めておくと、コードが安定します。
Number.isNaN をそのまま使ってもいいですが、名前を付けておくと意図がより明確になります。

function isNaNStrict(value) {
  return Number.isNaN(value);
}

console.log(isNaNStrict(NaN));           // true
console.log(isNaNStrict(10));            // false
console.log(isNaNStrict("abc"));         // false
console.log(isNaNStrict(Number("abc"))); // true
JavaScript

この関数は、「値そのものが NaN かどうか」を判定します。
「数値変換に失敗した結果の NaN を検出したい」という場面で使います。


数値変換と NaN 判定をセットで考える

NaN は、たいてい「数値変換に失敗した結果」として生まれます。
例えば、NumberparseInt, parseFloat を使ったときです。

const n1 = Number("10");     // 10
const n2 = Number("abc");    // NaN

console.log(Number.isNaN(n1)); // false
console.log(Number.isNaN(n2)); // true
JavaScript

実務では、「変換」と「NaN 判定」をセットで行うユーティリティを用意しておくと便利です。

function toNumberOrNull(value) {
  const n = Number(value);
  return Number.isNaN(n) ? null : n;
}

console.log(toNumberOrNull("10"));   // 10
console.log(toNumberOrNull("abc"));  // null
console.log(toNumberOrNull(""));     // 0(ここをどうするかは要件次第)
JavaScript

ここでは、「NaN になったら null にする」というルールを決めています。
こうしておくと、「NaN が静かに紛れ込む」のではなく、「null として扱う」と明示的に決められます。


計算結果に NaN が紛れ込んでいないかをチェックする

計算の途中で NaN が発生していないかをチェックするのも、実務ではよくやります。

function safeDivide(a, b) {
  const result = a / b;

  if (Number.isNaN(result)) {
    throw new Error("計算結果が NaN になりました");
  }

  return result;
}

console.log(safeDivide(10, 2));  // 5
console.log(safeDivide(10, 0));  // Infinity(ここは要件次第)
console.log(safeDivide(0, 0));   // ここで例外(NaN)
JavaScript

このように、「この時点で NaN が出ていたらおかしい」という場所で Number.isNaN を挟んでおくと、
バグを早期に検知しやすくなります。


NaN 判定と「数値判定」の違いを整理する

NaN 判定は、「その値が NaN かどうか」を見るものです。
一方で、「数値として扱えるかどうか」を見るユーティリティ(isNumeric のようなもの)とは役割が違います。

例えば、次のような関数を思い出してください(以前やった数値判定の発展形です)。

function isNumeric(value) {
  if (typeof value === "number") {
    return !Number.isNaN(value);
  }

  if (typeof value === "string") {
    if (value.trim() === "") return false;
    const n = Number(value);
    return !Number.isNaN(n);
  }

  return false;
}
JavaScript

ここでは、「NaN を除外するために Number.isNaN を使っている」ことが分かります。
つまり、NaN 判定は「数値判定の中の一部」としても重要な役割を持っています。


実務での具体的な利用イメージ

API からの数値レスポンスを検証する

API から数値が返ってくる想定なのに、実際にはおかしな値が混ざることがあります。

async function fetchPrice() {
  const res = await fetch("/api/price");
  const data = await res.json(); // 例えば { price: "abc" } が返ってきたとする

  const price = Number(data.price);

  if (Number.isNaN(price)) {
    throw new Error("API から不正な価格が返ってきました");
  }

  return price;
}
JavaScript

ここで NaN 判定を挟んでおくことで、「壊れた値がそのまま計算に使われる」のを防げます。

集計処理の途中で NaN を検出する

大量のデータを集計するときも、NaN が紛れ込んでいないかをチェックしたくなります。

function sum(values) {
  let total = 0;

  for (const v of values) {
    const n = Number(v);
    if (Number.isNaN(n)) {
      throw new Error(`数値に変換できない値があります: ${v}`);
    }
    total += n;
  }

  return total;
}

console.log(sum(["10", "20", "30"])); // 60
console.log(sum(["10", "abc", "30"])); // ここで例外
JavaScript

このように、「NaN を見つけたらすぐに止める」という方針を取ると、
「どこかで NaN が混ざっていたせいで、最終結果だけおかしい」という状況を避けられます。


小さな練習で感覚をつかむ

最後に、自分の手で NaN 判定を試してみると、理解が一気に深まります。

次の値を順番に Number.isNaN に渡して、結果をコンソールに出してみてください。

NaN, 0 / 0, 10 / 0, 10, "10", "abc", Number("abc"), undefined, null

そのうえで、「どのタイミングで NaN が生まれているか」「どこで NaN 判定を挟むべきか」を意識しながら、
自分のプロジェクトの数値処理に Number.isNaN を組み込んでいくと、数値まわりのバグがかなり減っていきます。

タイトルとURLをコピーしました