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" | 123 | true |
"" | 0 | true |
" " | 0 | true |
null | 0 | true |
true | 1 | true |
[] | 0 | true |
[42] | 42 | true(要素1つの配列は数値化される) |
"abc" | NaN | false |
undefined | NaN | false |
{} | NaN | false |
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.isFinite で NaN と Infinity を除外する流れ。
NaN / Infinity の扱い
Number.isFinite は NaN と ±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を使い、グローバル版を禁止するのが実務的