TypeScript(JavaScript)における数値チェック(isNaN)をeslintを使ってより安全に使う

# はじめに
以前に「[TypeScriptにおけるnullとundefinedそしてfalsy値](https://qiita.com/yoshitama/items/643a86f2cae744f43658)」という記事を書いた際に、
NaN === NaNがfalseになるという仕様を教えていただきました。
熟練者であれば当然の仕様かと思いますが、改めてまとめていきたいと思います。

# 結論
### TypeScript(JavaScript)で数値チェックをするときは`Number.isNaN()`を使いましょう。
```ts
const hoge = なにか;
if (Number.isNaN(hoge)) { console.log('数値ではありません'); }
```

### eslintでisNaN()を使えないように設定しよう
```eslintrc.yml
rules: {
  no-restricted-globals: [
    "error",
    {
      "name": "isNaN",
      "message": "Use Number.isNaN"
    },
  ]
}
```

# NaNとは
JavaScriptのドキュメント(以下MDN)によるとこのように記載されています。
> グローバルプロパティ NaN は非数 (Not-A-Number) を表す値です。

Numberではないとのことですがtypeofで出力してみるとnumber型だということになります。

```ts
console.log(typeof NaN); // number
```
MDNではこのように表記されています。
> ・数値が解釈できない (例えば parseInt("blabla") または Number(undefined))
・結果が実数にならない数学演算 (例えば Math.sqrt(-1))
・オペランドが NaN である (例えば 7 ** NaN)
・不確定形 (例えば 0 * Infinity または undefined + undefined)
・文字列が関わる加算以外の何らかの演算 (例えば "foo" / 3)

このことから、NaNはnumber型ではないという意味ではなく、 __数値としてあらわすことのできない数字__ ということになります。

# NaNを判別するにはNumber.isNaN()を使いましょう

### NaN === NaN はfalseになる

NaNはいかなる値と比較してもfalseになります。そのため別の比較方法が求められます。
```ts
console.log(NaN === NaN); // false
```
### isNaN()だとNaNかどうかを厳密に判断できない
`isNaN()`は`undefined`や`'hoge'`などでもnumber型に変換され`true`が返ってきてしまい、厳密に`NaN`かどうかを判断することができません。
```ts
isNaN(undefined); // true
isNaN('hoge');    // true
```

### Number.isNaN()であれば厳密に判断できる
`Number.isNaN()`は`number型`であり、かつ`NaN`である時にのみ`true`を返します。
```ts
Number.isNaN(undefined); // false
Number.isNaN('hoge');    // false
```

# 注意するべき点
### 指数表記のeが数値扱いになる
10のべき乗を`e + n`のように表記することがあり、一見数値ではないのですが、れっきとした数値の為、
TypeScript(JavaScript)ではnumber型の数値になります。
```ts
console.log(typeof 1e5); // number
```
### 0 / 0 もNumber.isNaN()ではtrueになる
当然ですが、`0 ÷ 0`もNaNになります。
また、`n ÷ 0`(nは0以外)の場合は(±)infinityになります。
```ts
console.log(Number.isNaN(0 / 0)); // true
```

# Number.isNaN()の代替()ポリフィル

### MDNで紹介されているポリフィル
`Number.isNaN()`は`es6`からの機能のため、古いブラウザなどでは動作しない可能性があります。
そのため、`Number.isNaN()`がなかった頃のイディオムとして以下のものが使用されていました。
※`NaN`はJavaScriptで唯一自分自身と比較して等しくない値という特性を利用したものです。
```ts
Number.isNaN = Number.isNaN || function isNaN(input) {
    return typeof input === 'number' && input !== input;
}
```

### 正規表現による数値チェック
[こちら](https://webllica.com/javascript-number-check-function/)で紹介されていた方法です。
自分がチェックしたい内容に合わせてカスタムして使えるので場面によっては使えると思います。
その中で`Number.isNaN()`に近いものを一つ紹介させていただきます。
```ts
function isNumber(numVal){
  // チェック条件パターン
  var pattern = /^[-]?([1-9]\d*|0)(\.\d+)?$/;
  // 数値チェック
  return pattern.test(numVal);
}
```

# eslintを利用してisNaNを禁止したい
このように`isNaN()`はいくつかバグを引き起こす可能性があるので、禁止したいのですが、
プログラマが使わないようにと気を付けても使ってしまうものです。こういう時は`eslint`を使って禁止にしちゃいます。

### 前提
・node.js
・eslint
上記二つを導入済み

:::note warn
eslintrcファイルは本記事ではymlですが、ご自身の環境によって書き方を変えていただければと思います。
:::

### NaNを直接比較できないように
何かしらの有名スタイルを継承しているのであれば設定をする必要は無いですが
一応こちらの設定があるか確認しましょう。なければ追加します。
```eslintrc.yml
rules: {
  use-isnan: "error"
}
```

### isNaNが使えないように
この設定でisNaNを使えないようにします。
```eslintrc.yml
rules: {
  no-restricted-globals: [
    "error",
    {
      "name": "isNaN",
      "message": "Use Number.isNaN"
    },
  ]
}
```
このように`isNaN()`を使用したときに`error`が出るようになりました。
```hoge.ts
const hoge = なにか;
console.log(isNaN(hoge));        // Unexpected use of 'isNaN'. Use Number.isNaN
console.log(Number.isNaN(hoge)); // 問題なし
```

# さいごに
他にも良い方法やご指摘などありましたら、コメントやリクエストをしていただければと思います。

# 参考
・[JavaScript の数値チェックに isNaN を使ってはいけない理由](https://webllica.com/dont-use-isnan-javascript-numeric-check/)
・[正規表現を用いて JavaScript で数値チェックを行う方法](https://webllica.com/javascript-number-check-function/)
・[MDN isNaN()](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/isNaN)
・[MDN Number.isNaN()](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Number/isNaN)
・[eslint use-isnan](https://eslint.org/docs/latest/rules/use-isnan)
・[eslint no-restricted-globals](https://eslint.org/docs/latest/rules/no-restricted-globals)