JavaScriptの数値チェックについてハマったのでメモ

## はじめに
こんにちは、普段は業務で求人系サービスの開発や社内向けツールの開発を行なっている@taku-0728です。
今回はJavaScriptの数値チェックについてまとめました。
調べると多くの記事がヒットしますが、true/falseを数値として扱ったりする記事を見つけたり色々なメソッドを見つけたりして混乱したので自分用メモとしてまとめます。

## 忙しい人向け
細かいことはいいから結論が知りたい忙しい人用に数値チェックについてまとめます。

| 値(val) | isNaN(val) | Number<br>.isNaN(val) | isFinite(val) | Number<br>.isFinite(val) | Number<br>.isInteger(val) |
| ---- | ---- | ---- | ---- | ---- | ---- |
| -1 | false | false | **true** | **true** | **true** |
| 0 | false | false | **true** | **true** | **true** |
| 1 | false | false | **true**  | **true** | **true** |
| '-1' | false | false | **true** | false | false |
| '0' | false | false | **true** | false | false |
| '1' | false | false | **true** | false | false |
| -0.1 | false | false | **true** | **true** | false |
| 0.1 | false | false | **true** | **true** | false |
| '-0.1' | false | false | **true** | false | false |
| '0.1' | false | false | **true**  | false | false |
| 012 | false | false | **true** | **true** | **true** |
| '012' | false | false | **true** | false | false |
| 0x12 | false | false | **true** | **true** | **true** |
| '0x12' | false | false | **true** | false | false |
| true | false | false | **true**  | false | false |
| false | false | false | **true**  | false | false |
| 'true' | **true** | false | false | false | false |
| 'false' | **true** | false | false | false | false |
| undefined | **true** | false | false | false | false |
| 'undefined' | **true** | false | false | false | false |
| null | false | false | **true** | false | false |
| 'null' | **true** | false | false | false | false |
| NaN | **true** | **true** | false | false | false |
| 'NaN' | **true** | false | false | false | false |
| Infinity | false | false | false | false | false |
| 'Infinity' | false | false | false | false | false |

## 解説
### isNaN()
[公式リファレンス](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/isNaN)より
> isNaN() 関数は引数が NaN （非数）かどうかを判定します。
> isNaN 関数の引数が 数値型ではない場合、その値はまず数へと型強制されます。

つまり、数字でない場合trueを返すということですが、**引数が数値型ではない場合、その値はまず数値型へと型変換された上で比較される**点に注意が必要です。
そのため上の表にもまとめていますが、以下のような条件式は全てtrueになってしまいます。

```js
isNaN('true')
isNaN('false')
isNaN(undefined)
isNaN('undefined')
isNaN('null')
```

上記のような暗黙な型変換を許容しない場合、Number.isNaN()を使うべきです。

### Number.isNaN()
[公式リファレンス](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Number/isNaN)より
> Number.isNaN() メソッドは、渡された値が NaN であり、かつその型が Number であるかどうかを判断します。元となるグローバルの isNaN() よりも堅牢な版です。

上記isNaN()とは暗黙な型変換を行わない点に違いがあります。
そのためNumber.isNaN()メソッドは下記の場合のみtrueを返します。

```js
Number.isNaN(NaN)
```

### isFinite()
[公式リファレンス](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/isFinite)より
> グローバル関数 isFinite() は渡された値が有限数かどうかを判定します。必要に応じて、引数はまず数値へと変換されます。

ただし、isNaN()と同様引数が数値型でなかった場合、暗黙な型変換が行われるため下記のような条件式は全てtrueになっていまいます。

```js
isFinite(true)
isFinite(false)
isFinite(null)
```
true/falseやnullを全て1や0として変換するため上記は全てtrueになってしまいます。
これらを許容しない場合、Number.isFinite()を使うべきです。

### Number.isFinite()
[公式リファレンス](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Number/isFinite)より
> Number.isFinite() メソッドは、引数として与えた数が有限数かどうかの真偽値を返します。

上記Number.isNaNメソッドと同様暗黙な型変換を行いません。

### Number.isInteger()
[公式リファレンス](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Number/isInteger)より
> Number.isInteger() メソッドは渡された値が整数かどうかを判定します。

Number.isFinite()と似ていますが、Number.isInteger()は整数のみtrueとするため引数に小数を渡すとfalseになります。
8進数や16進数でも整数ならばtrueになります。

### 正規表現
一番上の表には記載していませんが、正規表現を用いて数値チェックを行うこともできます。

```js
// 0以上の整数のみ
function isNumber(val){
  var regexp = new RegExp(/^[0-9]+(\.[0-9]+)?$/);
  return regexp.test(val);
}
```
目的に応じてRegExpの中身を書き換えてください

```js
// 整数 (- のみ許容)
var regexp = new RegExp(/^[-]?([1-9]\d*|0)$/);

// 整数 (+, – 許容)
var regexp = new RegExp(/^[+,-]?([1-9]\d*|0)$/);

// 符号なし小数
var regexp = new RegExp(/^([1-9]\d*|0)(\.\d+)?$/);

// 符号あり小数 (- のみ許容)
var regexp = new RegExp(/^[-]?([1-9]\d*|0)(\.\d+)?$/);

// 符号あり小数 (+, – 許容)
var regexp = new RegExp(/^[+,-]?([1-9]\d*|0)(\.\d+)?$/);
```

## 結論
ここまでまとめて結局どれを使えばいいの？と思われるかもしれませんが、これといった正解がないのが正直な感想です。
数値型のチェックをしたいのであれば小数を許容するかに応じてNumber.isFinite()か、Number.isInteger()を使うのが適切だと思います。
文字列として渡ってきた数値も許容したい場合、引数を数値型に変換したうえでNumber.isFinite()を使うか、正規表現を使うのが適切だと思います。
他にいい方法があればコメント等で教えていただければ助かります。

## 参考
[数値かどうか、型を判定する isNumber と、テスト駆動開発](https://zero-plus-one.jp/javascript/isnumber/)
[正規表現を用いて JavaScript で数値チェックを行う方法](https://webllica.com/javascript-number-check-function/)