[TypeScript] TS 4.1 の機能で雑に型の足し算・引き算を作る
2020年11月リリースの TypeScript 4.1 で「テンプレートリテラル型」(Template literal types)という機能が入ります(TypeScript #40336)。この機能を用いると、非常に雑に述べると「型レベルで文字列の解析ができる」ようになるため、型の表現方法がさらに拡がることが考えられます。
既に n 番煎じかもしれませんが、、この機能を使って「数値リテラル型」(および一部の「文字リテラル型」)に対して算術演算の足し算と引き算を作ってみました。
※ TS 4.8 の機能を用いた改良版はこちら → [TypeScript] 型の足し算・引き算を作る(再) + TS 4.8 の機能を利用
ソースコード
メインは「Add」と「Sub」の型になります。
※ Playgroundへのリンク → TS Playground (4.1.0-dev.20201003)
※ 以下に gist.github.com にアップロードしたソースコードを埋め込んでいます。表示されない場合はこちら → https://gist.github.com/jet2jet/5b06e87fd20d20a5dac93bf5b5965722
使い方
- ソースコードの「Sample1」~「Sample6」にあるように、Add / Sub それぞれの型にある2つの型変数(型引数)に、数値リテラル型(
123
など)または「中身が10進数」となっている文字列リテラル型('1010'
など)を指定します。 - あまり具体的な用途は思いついていませんが、、主に型が数値リテラル型となっている数値定数を用いて、それに対して何らかの数値を加算した新たな定数を作りたい場合に、その定数の型を
number
よりも具体的な型にする際に用いることができます。(上記ソースコード中の sample7 や sample8 が該当します。)
制限事項など
- 小数点以下の数値リテラルには対応していません。
- このコードでは「文字列リテラルを数値リテラルに変換」するために Tuple を用いているため、負数になる数値リテラルの生成には対応しておらず、その場合は文字列リテラルになります。(Sample5 が該当します。そのため、減算が入る場合予期しない型になる可能性があります。)
- あまりに桁数が多くなったり加算減算回数が多くなったりすると、コンパイラーの処理が重くなったりエラーになったりする可能性があります。
詳細
- 「Add」では、AとBがそれぞれ負数かどうかをチェックし、ともに負数であれば SubInner を、そうでなければ AddInner を参照します。この際、型変数にはAとBを1文字ずつ(の文字列リテラル型)に分解した Tuple 型を指定するようにしています。
- AddInner では、AとBそれぞれに MakeZeroTuple を適用し、それを結合しています。
- MakeZeroTuple は、「NumericChars の Tuple かどうか」を念のためチェックしたのち、Tuple の末尾から順に NumericChars を取り出して
MakeTuplesMap<['0']>[V]
(V
は NumericChars) を適用し、それを結合しています。- MakeTuplesMap には NumericChars に並べている文字列リテラルと同じ名前のフィールドを持ち、それらの型は「型変数に与えられた Tuple をその数値の回数だけ繰り返した型」としています。(例:
MakeTuplesMap<['0']>['4']
は['0', '0', '0', '0']
という Tuple になります。) - さらに、末尾を削った残りに対して再帰的に MakeZeroTuple を適用しつつ、その結果を MakeTuplesMap の型変数に与えることで、いわゆる「10倍」をできるようにしています。
- MakeTuplesMap には NumericChars に並べている文字列リテラルと同じ名前のフィールドを持ち、それらの型は「型変数に与えられた Tuple をその数値の回数だけ繰り返した型」としています。(例:
- 結果、MakeZeroTuple は「'0'」を指定の個数だけ含む Tuple に変換するジェネリック型となります。
- MakeZeroTuple は、「NumericChars の Tuple かどうか」を念のためチェックしたのち、Tuple の末尾から順に NumericChars を取り出して
- SubInner では、AとBそれぞれの MakeZeroTuple の結果を(便宜上条件付き型を用いて) RA と RB に入れ、「RA が RB と『何か』(= R)の結合したものになる」のであれば R の Tuple の長さを、「RB が RA と『何か』(= R)の結合したものになる」のであれば「R の Tuple の長さ」を文字列化して先頭に「-」を付けた型、となるような型の変換をします。
- AddInner では、AとBそれぞれに MakeZeroTuple を適用し、それを結合しています。
- 「Sub」は、「Add」の中に既に減算処理が含まれているので、Bの符号を反転させて「Add」を呼び出すだけとしています。
その他・ライセンス
- ソースコード冒頭にリンクしているライセンスにもあるように、本ソースコードのライセンスは gist jet2jet/LICENSE に従います。(2020年10月時点では0条項BSDライセンスとしており、現時点で変更の予定はありません。)
- ソースコードに対して誤り等がありましたら、gist 上のコメント欄にてコメントをいただければと思います。(「当サイトについて」からリンクしているメールフォームからでも構いません。)
更新履歴
- 2020/10/08 - サンプルの
add
関数において enum / const enum の値も扱えるように対応し、戻り値が文字列リテラルにならないように修正。その他細かい誤字を修正 - 2020/10/04 - 作成