🤔

Distributive Conditional Type(DCT)って何・・・

に公開

経緯

AI 「いいか、良く聞け。TSにはDCTというものがあるのじゃ」
risk 「え、なんですかそれは・・・」

というわけで、Distributive Conditional Type 略して DCT の話です。
分配条件型というらしく、言葉は知りませんでしたが、勝手に使ってました。(あかんね)

中身の話

サンプルコードを書いていくと、あ〜これかぁ・・・となったんですが
実際のところ、なんでそんなふうになるのか、ほんとに理解してませんでした。
面白い話なので興味がある方は、ぜひお試しください。

コード

https://github.com/risk/ts-playground/blob/main/src/distributiveConditionalType/distributiveConditionalType.ts

まずはUnion型の定義

DCTは、Unionに対して発生する分配挙動になります。
https://github.com/risk/ts-playground/blob/56fd7ecff54d55dd5bfcaed6561786373c58d7c6/src/distributiveConditionalType/distributiveConditionalType.ts#L9

基礎編

DCTとは、Unionに対してextendsを使う場合、Unionのそれぞれの型に対して評価を行い、再結合するという動作を指すそうです。

百聞は一見にしかず。コードを見てもらったりお手元のVSCode等で型を確認いただくと、わかるかなと思います。
https://github.com/risk/ts-playground/blob/56fd7ecff54d55dd5bfcaed6561786373c58d7c6/src/distributiveConditionalType/distributiveConditionalType.ts#L11-L23

Tに指定したUnionのそれぞれの要素で評価を行い、それらを再結合しているところが見られるかと思います。

分配されたくないんだ という方

[]で評価する型を明示的にくくってあげてください、そうするとUnionのまま評価したり引き渡すことが出来ます。
https://github.com/risk/ts-playground/blob/56fd7ecff54d55dd5bfcaed6561786373c58d7c6/src/distributiveConditionalType/distributiveConditionalType.ts#L25-L33

ここまでのまとめですが...

型がUnionの場合、ForEach的な挙動をして、型の評価と再結合をする感じ(reduceっぽいのかな)
https://github.com/risk/ts-playground/blob/56fd7ecff54d55dd5bfcaed6561786373c58d7c6/src/distributiveConditionalType/distributiveConditionalType.ts#L35-L43

応用編

みんな大好き(?)Extract と Exclude をネタにしまして、DCTを見てみましょう
https://github.com/risk/ts-playground/blob/56fd7ecff54d55dd5bfcaed6561786373c58d7c6/src/distributiveConditionalType/distributiveConditionalType.ts#L45-L62

どちらも、DCTの仕組みを活用して作られてるんですねぇ。

never の話

neverってUnion型に含めると、消滅するんすね。
https://github.com/risk/ts-playground/blob/56fd7ecff54d55dd5bfcaed6561786373c58d7c6/src/distributiveConditionalType/distributiveConditionalType.ts#L64-L66

AIさんが教えてくれた、さらなる応用

構造体の中身に分配していく仕組みも書いとけ って教えてくれたから試してたら
https://github.com/risk/ts-playground/blob/56fd7ecff54d55dd5bfcaed6561786373c58d7c6/src/distributiveConditionalType/distributiveConditionalType.ts#L68-L76
AIさんと結果が違っとる・・・

AIさんは

{ kind: 'other', value: boolean }

になると教えてくれたのですが、実際には

{ kind: 'other', value: false } | { kind: 'other', value: true }

こうでてきました。booleanってプリミティブなのに分解されんの・・・
何やらBooleanの挙動が面白いことになってますので、掘りました。

Boolean深堀り

https://github.com/risk/ts-playground/blob/56fd7ecff54d55dd5bfcaed6561786373c58d7c6/src/distributiveConditionalType/distributiveConditionalType.ts#L78-L84
確かに、書き方次第では、Booleanで返ってくることあるのかと確認していると

AI「それ、結合されてBooleanに戻ってっから」
risk「そんなことあるんですかい・・・」
https://github.com/risk/ts-playground/blob/56fd7ecff54d55dd5bfcaed6561786373c58d7c6/src/distributiveConditionalType/distributiveConditionalType.ts#L86-L88
ほんとだ・・・

最後に

DCTはいかがでしたでしょうか。型を扱ってると意図せず使っているケースが多いように思いますが、この辺の恩恵を受けて、なんかできちゃった型とかが生まれてるなぁと感じています。
細かな仕組みではありますが、覚えておくと便利かもしれません。

Discussion