JavaScript or TypeScriptで大きい数字を扱うときは罠がいっぱい。 #zod - Qiita

どうもこんにちは 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ですね。

スクリーンショット 2025-04-18 16.37.45.png

小数点の方も似たような現象でした。

そこで今回の対応としては次のようにしました。

文字列で受け取るようにして、文字列の正規表現でバリデートする方法です。

これで無事動作しました。

例えば以下のような計算式、出力はどうなると思いますか。

ちなみに数字は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″として対応したらうまく行った。

昔の誰か(もしかしたら自分が) ||と書き間違えたのかもしれない。

とにかくよかった。

最近、本番落とすことが多くてなかなか神経質になっていたから

コードをより注意深くみる癖がついたことによる発見だと思う。

気を抜いていたわけではなかったが、これからも継続していきたい



フラッグシティパートナーズ海外不動産投資セミナー 【DMM FX】入金

Source link