Panda Noir

JavaScript の限界を究めるブログでした。最近はいろんな分野を幅広めに書いてます。

TypeScript の infer は同じ名前を指定できる

(ただし有用な場面はそんなにないです)

infer に同じ名前を指定すると、ユニオン型になる

基本的に、infer に同じ名前を指定すると、それらをうまく満たすようなユニオン型になります。

以下の X1,X2,X3 はすべて number | string です。

type X1 = [number, string] extends [infer x, infer x] ? x : never;
type X2 = [number[], string] extends [(infer x)[], infer x] ? x : never;
type X3 =
  [(args: number) => number, string] extends [(args: any) => infer x, infer x] ?
    x
  : never;

関数の引数に対しては例外的な挙動をする

上に書いたように、基本的にはユニオンになるのですが、関数の引数部分で infer x を入れると異なる挙動をします。

type X4 =
  [(args: number) => number, string] extends [(args: infer x) => any, infer x] ?
    x
  : never; // never になる
type X5 =
  [(args: number) => number, (args: string) => number] extends (
    [(args: infer x) => any, (args: infer x) => any]
  ) ?
    x
  : never; // これも never になる

おそらく関数の引数が反変であることに起因しています。

TypeScript の実際のコードを読んでないので以下は僕の推察なのですが、おそらく 最初の infer x (区別するため x1 とします)と2つ目の infer x (x2) は、 x1 <: x2 かつ x1 >: x2 を満たす必要があるとか、そういう理由なんじゃないかと思ってます。ちょっと分からないのでどなたか調査お願いします。

参考

共変性・反変性とは型構築子が部分型関係をどう保ち、どう変換するかという性質のことである