どうもこんにちは torihaziです。
今日はTypeScriptのnumberを扱っていた時に
少し詰まったのでその備忘録になります。
特に formで大きな数値を扱ったり、表示したりするときにハマる可能性大です。
今回はreact-hook-formとzodを組み合わせて使用した例を出しています。
例えば 以下のような数値(整数10桁、小数点2桁まで)を入力するformとzodのスキーマがあったとします。
input type=numberを使用しているformです。
ここに例えば 100000000000000000000 (1垓 = 0が20個)を入力します。
その数値までは確かに機能するのでzodのバリデーションも機能するのですが、
そこから0を1個増やすとこの数字は科学的記法に変換されます。
どういうことかというと
1000000000000000000000 (0が21個) => 1e+21
として扱われます。
これが起きるとどうなるかというと、
先ほどのzodのルールをpassします。それもそのはず。
1e+21を toStringした後に “.”でsplitして その後lengthを測るのだから
結果的に 1e+21のlength = 5
これ、useFormのwatchを使って0を書き足して行った時のlogですね。
小数点の方も似たような現象でした。
そこで今回の対応としては次のようにしました。
文字列で受け取るようにして、文字列の正規表現でバリデートする方法です。
これで無事動作しました。
例えば以下のような計算式、出力はどうなると思いますか。
ちなみに数字は100億です。
console.log(10000000000|0)
答えは。
console.log(10000000000|0)
=> 1410065408
なぜでしょうか。
問題の鍵は |(ビット演算子(OR)) にあります。
オペランドは 32 ビットの整数値に変換され、ビット (ゼロまたは 1) の並びによって表現されます。32 ビットを超える数値は最上位のビットが破棄されます。例えば、次の 32 ビットを超える整数は 32 ビット整数に変換されます。
どういうことかというと
1. 10000000000|0 の時点で どちらも32ビットに変換する
2. 10000000000を32ビットに変換
3. しようとしたけど32ビットの最大値を超えてるから切り捨て。
4. 1001010100000010111110010000000000 (34ビット)なので先頭2つを切り捨て
5. 10を切って 01010100000010111110010000000000となる。
6. 残った方を計算すると 1410065408
7. 10000000000|0 => 1410065408 として出力。
ということ。
実際、データベースにはちゃんと100億が記録されていたので
よかったものの少し焦った。
解決策としてはビット演算子を使わなければいいだけのこと。
コード読んでみると “変数 | 0″みたいな書き方をされており、
おそらく undefinedとかだったら 0にするロジックだと判断したので
“変数 ?? 0″として対応したらうまく行った。
昔の誰か(もしかしたら自分が) ||と書き間違えたのかもしれない。
とにかくよかった。
最近、本番落とすことが多くてなかなか神経質になっていたから
コードをより注意深くみる癖がついたことによる発見だと思う。
気を抜いていたわけではなかったが、これからも継続していきたい
Views: 0