TypeScriptにInfinity型は存在する?
TypeScriptにはリテラル型という機能があるので、数値を型として使うことができます。
では特殊な数値Infinity
に対応するInfinity型は存在するのか? がこの記事のテーマです。
結論だけ先に書くと「Infinity型は表面上は提供されていないが内部的には存在する」という感じの不思議な状況のようです。
ちょっとややこしい話なので、順を追って説明していきます。
Infinity
はリテラルではない
そもそも実はInfinity
はグローバル変数らしいです。
試しにVSCodeでInfinity
の定義にジャンプしてみると次のように型定義されています。
このことから次のことが分かります。
-
Infinity
はリテラルではないので、リテラル型としてのInfinity型も存在しない - 変数
Intifity
の型はnumber
として定義されているので、typeof Infinity
をInfinity型として使うこともできない
Infinityという名前の型は標準で定義されてない
Infinity
という名前の組み込み型が用意されていないことも確認しておきましょう。
おもむろにInfinity型を使おうとすると次のように型エラーが出ます。
(逆に言うと、Infinity
という名前の型はユーザーが自由に定義できちゃったりします)
さて、ここまでの話だとTypeScriptという言語はInfinity
型のことなど一切気にしていなさそうに思えますが…🤔
Infinity
型が存在する
TypeScript処理系内部には少し状況をイメージしにくいかもしれませんが、実はTypeScript処理系にはInfinity
型が内部的に存在する痕跡があります。
こちらのスクリーンショットをご覧ください。
ソースコード内には一切Infinity
と書いていないのに、メッセージ内にInfinity
という型名が出てきます。
つまり、TypeScript処理系は上記の1e+999
という型を、内部的にInfinity
型として扱っているわけです。
TypeScriptにおける指数表記
1e+999
はいわゆる指数表記で、1
の後に0
が999個続く超巨大な数値です。
数式で書くと
TypeScript/JavaScriptではこの表記で数値リテラルを書くことができます。
console.log(1e+3) // 1000
console.log(2.5e+5) // 250000
ちなみにソースコード上の表記が指数表記であろうとなかろうと、一定の大きさを超える数値は指数表記で表示されることを頭の片隅の置いておくと良いかもしれません。
console.log(1e+20) // 100000000000000000000
console.log(1e+21) // 1e+21
console.log(1000000000000000000000) // 1e+21
number型の上限値とInfinityの関係
TypeScriptのnumber型が扱える数値の範囲には次のような上限があります。
console.log(Number.MAX_VALUE) // 1.7976931348623157e+308
実はこの上限値1.7976931348623157e+308
より大きい数値リテラルを書くと、Infinityになります。
下記のコードで実際に確認できます。
console.log(1.8e+308) // Infinity
console.log(1.8e+308 === Infinity) // true
console.log(1.8e+308 === 1e+999) // true
上記は値レベルの話ですが、型レベル(数値リテラル型)でも全く同じ現象が起こります。
つまり、1.7976931348623157e+308
より大きいリテラル型を書くと、Infinity型になるわけです。
先ほど掲載していたスクリーンショットの中で1e+999
というリテラル型がInfinity
型と表示されたのがその証拠です。
Infinity型をユーザーが定義するとどうなるか
ここまでの話を踏まえると、例えば次のようにInfinity
型を定義できます。
export type Infinity = 1e+999
これで問題解決…と言いたいところですが、実は負の無限大-Infinity
型の方は全然うまくいきません。
まず、型の世界では-
演算子はサポートされていません。使おうとすると次のようにエラーになってしまいます。
また-Infinity
という名前の型を直接定義することも不可能です。-
は識別子に使えない文字だからです。
export type -Infinity = -1e+999 // ❌
export type NegativeInfinity = -1e+999 // ✅
そういう問題があるので、Infinity
型をユーザーが定義しても少し不満が残りそうです。
やはり公式にサポートしてもらわないと解決できそうにないです。
TypeScript公式リポジトリのissue
Infinity
型が無い件の公式issueは何年も前からあるようですが、残念ながら大きな動きはありません😢
しかしハイレベルな型付けを実用するライブラリなどがここ数年でも増えていると思うので、Infinity
型サポートの需要が高まって新たな動きが生まれる可能性も0ではない気がします。
多対多の関係を表現するときなどに使える0 | 1 | Infinity
型や、timeout引数にInfinity
を与えた時の特別な挙動を型レベルで表現するなど、一応用途はあると思うので個人的にはこっそり期待しています。
ちょっと株式会社(chot-inc.com)のエンジニアブログです。 フロントエンドエンジニア募集中! カジュアル面接申し込みはこちらから chot-inc.com/recruit/iuj62owig
Discussion
そのIssueを立てた本人ですが、全然動きがなくてもどかしいですね…。
正確には「内部にInfinity型が存在する」というわけではないと思っています。
const hoge: 1e+999 = 42
のようなことをすると、途中までは内部的には単なるnumberのリテラル型として扱おうとします。そして、1e+999
という型を文字列で表現しようとする際の変換で、ぱっと見ではまるで内部に特別なInfinity型が存在するように見えてしまっている…というだけの話だと思います。それでも無理矢理作ったこの型が「事実上のInfinity型」として期待通りに動いてくれれば良いのですが、実際にはこの型はまともに動作しません。
ちなみに、「
Infinity
はtrue
やfalse
やnull
と違い、厳密にはリテラルではない」というのは全くその通りなのですが、それはundefined
も同様です。Infinity
もundefined
もNaN
も、「演算の結果で生まれることがある特別な値だが、仕様上は特別な値が入ったグローバル定数に過ぎない」という意味では同様です。undefined
をリテラル型として扱える以上、Infinity
もそうできないのは純粋に奇妙な制限だなと思っています…。