FindFromUnion<R, Key, Value>
recordのunionから1つを抽出する
以下は同じになる
tstype A = FindFromUnion<TrafficLight, 'light', 'red'>;
type B = Extract<TrafficLight, { light: 'red' }>;
定義
tstype FindFromUnion<
R extends Record<string, unknown>,
Key extends keyof R,
Value extends R[Key]
> = Extract<R, Record<Key, Value>>;
利用例
tstype TrafficLight =
| { light: "red"; action: "stop" }
| { light: "yellow"; action: "slow down" }
| { light: "green"; action: "go" };
type A = FindFromUnion<TrafficLight, "light", "red">; // { light: "red"; action: "stop" }
union型 TrafficLight
から、 light === 'red'
なものを抽出している
switch文に型をつける例
tsexport const actionFromLight = <Light extends TrafficLight["light"]>(
light: Light
): FindFromUnion<TrafficLight, "light", Light>["action"] => {
switch (light) {
case "red":
return "stop" as any;
case "yellow":
return "slow down" as any;
case "green":
return "go" as any;
default:
return exhaustiveCheck(light);
}
};
↑型推論の問題で as any
を付けないとエラーになる
これ、
TrafficLight
の内容が変わった時にエラーにならないので渋い...

こうすれば、引数から返り値が決定する
tsconst action = actionFromLight("red"); // 'stop'
定義の意味
例えば、簡易化した以下のような型を見る
tstype Red = Extract<TrafficLight, { light: "red" }>;
// ↑{ light: "red"; action: "stop" }
これは以下を全て抑えていないと理解できない
tstype Extract<U, G> = U extends G ? U : never;
U
が G
の部分型なら U
を返す
今回は U
がUnion型なので、dirstributeして評価される
{ light: "red"; action: "stop" }
<: { light: "red"}
だということ
従って、
{ light: "red"; action: "stop" } extends { light: "red"}
が真となり、
部分型の方である { light: "red"; action: "stop" }
が返る