LoginSignup
1
0

More than 3 years have passed since last update.

TypeScriptで関数の引数に型変数とConditional Typesを使うテクニック

Last updated at Posted at 2021-04-23

誰も書いてる人がいなかったので。執筆時点のTypeScriptのバージョンは4.2.3です。

以下の関数を見てください。

function f<T>(x: T extends string ? never : T) { return x }

このシグネチャが型レベルでどう動作するのか、ぱっと見でわかりますか? 初めて見たとき筆者はわかりませんでした。しかし、ちょっと考えてみればなんとなく予想はつくでしょう。この引数xは、string型を受け取ろうとしているときだけnever型として振舞う(つまりstring型だけを禁止する引数となる)という挙動をとります。このことの利点は、まだ真にTSに入ってはいないNegated Typesを(関数の引数の位置に限って)表現できる、ということももちろんですが、もっと重要なのは以下の点です。たとえば引数の代わりに返り値にConditional Typesを使用して:

function g<T>(x: T): T extends string ? never : T { return x as any }

このようにした場合、関数の呼び出し時にTSにエラーを吐かせることができません。その関数のneverな返り値を実際に他のところで使おうとしたときに初めて型の異常が発覚します(しかもnever型の値はどんな型の変数にも入れられるので、もしかしたら異常が発覚しないままかもしれません)。

f(42)
f("42") // Argument of type 'string' is not assignable to parameter of type 'never'.
g(42)
g("42") // NO ERROR HERE!!!!!!?!?!??!?!?!?!!?!!!!?!??!

これでは遅いと感じられる場合、引数の位置でConditional Typesを使うことを考えてください。

筆者がぱっと見でわからなかった原因は、型判断の結果がどう扱われるのかが、他の位置におけるそれと異なるからです。通常、A extends B ? C : DCDがそのまま型変数への束縛になることはありません。しかし引数の位置では、驚くべきことにそれが可能なのです。しかもこの束縛は式の外側の型変数に対して遡及的に起こっているのが面白いところです。

TS Playground

1
0
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
1
0