はじめに
以前に「TypeScriptにおけるnullとundefinedそしてfalsy値」という記事を書いた際に、
NaN === NaNがfalseになるという仕様を教えていただきました。
熟練者であれば当然の仕様かと思いますが、改めてまとめていきたいと思います。
結論
TypeScript(JavaScript)で数値チェックをするときはNumber.isNaN()
を使いましょう。
const hoge = なにか;
if (Number.isNaN(hoge)) { console.log('数値ではありません'); }
eslintでisNaN()を使えないように設定しよう
rules: {
no-restricted-globals: [
"error",
{
"name": "isNaN",
"message": "Use Number.isNaN"
},
]
}
NaNとは
JavaScriptのドキュメント(以下MDN)によるとこのように記載されています。
グローバルプロパティ NaN は非数 (Not-A-Number) を表す値です。
Numberではないとのことですがtypeofで出力してみるとnumber型だということになります。
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になります。そのため別の比較方法が求められます。
console.log(NaN === NaN); // false
isNaN()だとNaNかどうかを厳密に判断できない
isNaN()
はundefined
や'hoge'
などでもnumber型に変換されtrue
が返ってきてしまい、厳密にNaN
かどうかを判断することができません。
isNaN(undefined); // true
isNaN('hoge'); // true
Number.isNaN()であれば厳密に判断できる
Number.isNaN()
はnumber型
であり、かつNaN
である時にのみtrue
を返します。
Number.isNaN(undefined); // false
Number.isNaN('hoge'); // false
注意するべき点
指数表記のeが数値扱いになる
10のべき乗をe + n
のように表記することがあり、一見数値ではないのですが、れっきとした数値の為、
TypeScript(JavaScript)ではnumber型の数値になります。
console.log(typeof 1e5); // number
0 / 0 もNumber.isNaN()ではtrueになる
当然ですが、0 ÷ 0
もNaNになります。
また、n ÷ 0
(nは0以外)の場合は(±)infinityになります。
console.log(Number.isNaN(0 / 0)); // true
Number.isNaN()の代替()ポリフィル
MDNで紹介されているポリフィル
Number.isNaN()
はes6
からの機能のため、古いブラウザなどでは動作しない可能性があります。
そのため、Number.isNaN()
がなかった頃のイディオムとして以下のものが使用されていました。
※NaN
はJavaScriptで唯一自分自身と比較して等しくない値という特性を利用したものです。
Number.isNaN = Number.isNaN || function isNaN(input) {
return typeof input === 'number' && input !== input;
}
正規表現による数値チェック
こちらで紹介されていた方法です。
自分がチェックしたい内容に合わせてカスタムして使えるので場面によっては使えると思います。
その中でNumber.isNaN()
に近いものを一つ紹介させていただきます。
function isNumber(numVal){
// チェック条件パターン
var pattern = /^[-]?([1-9]\d*|0)(\.\d+)?$/;
// 数値チェック
return pattern.test(numVal);
}
eslintを利用してisNaNを禁止したい
このようにisNaN()
はいくつかバグを引き起こす可能性があるので、禁止したいのですが、
プログラマが使わないようにと気を付けても使ってしまうものです。こういう時はeslint
を使って禁止にしちゃいます。
前提
・node.js
・eslint
上記二つを導入済み
eslintrcファイルは本記事ではymlですが、ご自身の環境によって書き方を変えていただければと思います。
NaNを直接比較できないように
何かしらの有名スタイルを継承しているのであれば設定をする必要は無いですが
一応こちらの設定があるか確認しましょう。なければ追加します。
rules: {
use-isnan: "error"
}
isNaNが使えないように
この設定でisNaNを使えないようにします。
rules: {
no-restricted-globals: [
"error",
{
"name": "isNaN",
"message": "Use Number.isNaN"
},
]
}
このようにisNaN()
を使用したときにerror
が出るようになりました。
const hoge = なにか;
console.log(isNaN(hoge)); // Unexpected use of 'isNaN'. Use Number.isNaN
console.log(Number.isNaN(hoge)); // 問題なし
さいごに
他にも良い方法やご指摘などありましたら、コメントやリクエストをしていただければと思います。
参考
・JavaScript の数値チェックに isNaN を使ってはいけない理由
・正規表現を用いて JavaScript で数値チェックを行う方法
・MDN isNaN()
・MDN Number.isNaN()
・eslint use-isnan
・eslint no-restricted-globals