Number.isFinite と isFinite の違い ― JavaScript で「有限の数か」を判定する正しい書き方

結論

JavaScript で「有限の数値か」を判定するなら、Number.isFinite(x) を使う。グローバルの isFinite(x) は引数を Number に型強制してから判定するため、isFinite("123") のような文字列で true を返してしまう。

isFinite("123")          // ✅ true(文字列なのに true)
isFinite("")             // ✅ true(空文字なのに true、"" は 0 に変換される)
isFinite(true)           // ✅ true(boolean なのに true)

Number.isFinite("123")   // ❌ false
Number.isFinite("")      // ❌ false
Number.isFinite(true)    // ❌ false
Number.isFinite(123)     // ✅ true
Number.isFinite(NaN)     // ❌ false
Number.isFinite(Infinity)// ❌ false

ES6 で導入された Number.isFinite型強制を行わない。引数が Number 型でなければ無条件で false を返す。これが「安全な版」と呼ばれる理由。


なぜ global の isFinite は信用できないのか

ES5 以前から存在する isFinite() は、内部で ToNumber(x) を呼んでから有限性を判定する。ToNumber の挙動が以下のように直感に反するため、文字列や真偽値で予期せず true が返る。

入力ToNumber の結果isFinite の戻り値
"123"123true
""0true
" "0true
null0true
true1true
[]0true
[42]42true(要素1つの配列は数値化される)
"abc"NaNfalse
undefinedNaNfalse
{}NaNfalse

isFinite([])true を返すのは、ほぼ確実にバグの温床になる。


実用例:入力バリデーション

ユーザー入力(フォームから来た値)が「有限の数値か」を確認したい場面で isFinite を使ってはいけない。

// ❌ NG: 文字列のまま判定して true になる
function isValidPrice(value) {
  return isFinite(value)
}
isValidPrice("123abc")  // false("123abc" → NaN)
isValidPrice("123")     // true ← これが嬉しくない(数値ではなく文字列)
isValidPrice("")        // true ← これは絶対嬉しくない

// ✅ OK: 明示的に Number 変換してから判定
function isValidPrice(value) {
  const num = Number(value)
  return Number.isFinite(num)
}
isValidPrice("123abc")  // false
isValidPrice("123")     // true
isValidPrice("")        // false("" → 0 だが、Number.isFinite(0) は true なので注意)

最後のケースで注意。""Number("") すると 0 になり、Number.isFinite(0)true。つまり「空文字を有効な数として受け入れたくない」場合は、Number.isFinite の前に空文字チェックを入れる必要がある。

function isValidPrice(value) {
  if (typeof value === 'string' && value.trim() === '') return false
  const num = Number(value)
  return Number.isFinite(num)
}

TypeScript での型ガードとの組み合わせ

TypeScript で「unknown から number への型絞り込み」をしたい場合、Number.isFinite を型ガードに使うと安全。

function isFiniteNumber(value: unknown): value is number {
  return typeof value === 'number' && Number.isFinite(value)
}

const x: unknown = 42
if (isFiniteNumber(x)) {
  // ここで x は number として扱える
  console.log(x.toFixed(2))
}

typeof value === 'number' を先にチェックする理由は、Number.isFinite 自体は型ガードではないため。typeof で number 型に絞ったうえで Number.isFiniteNaNInfinity を除外する流れ。


NaN / Infinity の扱い

Number.isFiniteNaN±Infinity を確実に false と判定する。これが「安全」と呼ばれる重要な理由。

Number.isFinite(NaN)        // false
Number.isFinite(Infinity)   // false
Number.isFinite(-Infinity)  // false
Number.isFinite(0)          // true
Number.isFinite(-0)         // true
Number.isFinite(0.1 + 0.2)  // true(浮動小数の誤差はあるが有限)

NaN を弾く目的だけなら Number.isNaN(x) でも良いが、Infinity も同時に弾きたい場面の方が多いので、最初から Number.isFinite を覚えておく方が実務的。


ESLint で global の isFinite を禁止する

実プロジェクトでは、ESLint の no-restricted-globals ルールでグローバルの isFinite を禁止しておくのが安全。

{
  "rules": {
    "no-restricted-globals": [
      "error",
      {
        "name": "isFinite",
        "message": "Use Number.isFinite instead."
      },
      {
        "name": "isNaN",
        "message": "Use Number.isNaN instead."
      }
    ]
  }
}

isNaN も同じ理由(型強制)で global 版が危険なので、合わせて禁止しておくと良い。


まとめ

  • 「有限の数か」を判定するなら Number.isFinite(x) を使う
  • グローバルの isFinite(x)型強制で文字列・真偽値・配列も true にする
  • 空文字("")を弾きたいなら、Number.isFinite の前に明示チェックを入れる
  • TypeScript では typeof === 'number' と組み合わせた型ガードにすると安全
  • ESLint で no-restricted-globals を使い、グローバル版を禁止するのが実務的

関連リンク

関連記事