LoginSignup
23
9

More than 1 year has passed since last update.

TypeScriptで型推論やりすぎな足し算をする関数を作ってみた

Posted at

こんにちは。最近TypeScriptの型レベルプログラミングにハマっています。

今回実装する「型推論やりすぎ足し算」をする関数はこちらです。
既出だったらごめんなさい。

(画像が読み込めない時用のコード)

// 実装省略
declare function add<T extends number, U extends number>(a: T, b: U): Add<T, U>;
const a = add(12, 34); // testの型が46になる

a46 になり、型レベルでも足し算をして、その結果を返します
Add<T, U> という型は用意されていないので、これを工夫して作ります。

TypeScriptには、リテラル型というのがあり、 "hello"10true などの値も型となりえます。
このようにして、型レベルでも足し算を行うのが、この「やりすぎ」関数となっています。

ちなみに、普通にジェネリクスを使って実装しても、返り値は number となってしまいます。

function add2<T extends number, U extends number>(a: T, b: U) {
  return a + b;
}
const b = add2(12, 34); // 型はnumber

この a + b という演算が返す型はリテラル型ではなく number にされてしまうのですね。

使用したTypeScriptのバージョン

TypeScript 4.2.3
型レベルプログラミングは、TypeScriptのバージョンの違いをもろに受けるので、気を付けてください。

足し算部分の実装

まず、型 Add<T, U> の実装ですが、ここでは簡単に載せます。

type Repeat<T extends number, R extends any[] = []> = R["length"] extends T ? R : Repeat<T, [any, ...R]>;
type Add<T extends number, U extends number> = [...Repeat<T>, ...Repeat<U>]["length"];

Repeatで任意長(ただし再帰制限のため45程度まで)のTupleを作って、それを用いて足し算します。Tupleの長さが数値リテラル型で返ってくることから、それを足し算結果とします。

え、どうしてわざわざTupleを用いているの?などの背景の説明については、
ブログの記事で詳しく説明したので、そちらをご覧ください。

参考はいつもお世話になっているこのサイトのページです。

なお、再帰制限を回避してもっと大きな数まで扱いたい場合は、 Multiple<1, ...> を用いる手があります。内部的に文字列操作を行っているのです。この実装はややこしく、ちょっとテーマとそれるので、先ほどの記事を参照してください。

関数部分の実装

後は、これを関数の定義に含めれば完成です。

declare function add<T extends number, U extends number>(a: T, b: U): Add<T, U>;

T,Uは実は引数に応じてリテラル型に推論してくれるみたいです。助かった。

これで、冒頭の関数が実装できました。

実装を含める場合は、次のようにします。

a + bnumber 型となってしまうので、 as any as が必要です。

function add<T extends number, U extends number>(a: T, b: U): Add<T, U> {
  return a + b as any as Add<T, U>;
}

まとめ

型レベルプログラミングが、実際のプログラミングにも役に立ってくるともっと楽しいですね。
この関数の使いどころがあるかどうかはわかりませんがw

今回のプログラムは、こちらで試すことができます。

23
9
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
23
9