判別可能なユニオン (discriminated union)
次のようなタイプエイリアスのSuccessResponse, ErrorResponse
を考え、そのユニオン型としてResponse
を考えます。
typescript
type SuccessResponse = {success: true;response: Data;};type ErrorResponse = {success: false;error: Error;};type Response = SuccessResponse | ErrorResponse;
typescript
type SuccessResponse = {success: true;response: Data;};type ErrorResponse = {success: false;error: Error;};type Response = SuccessResponse | ErrorResponse;
ユニオン型のResponse
はふたつのタイプエイリアスが持つsuccess
を共通のプロパティとして持ちますが片方はtrue
でもう片方はfalse
です。ここはboolean
型とせずあえてリテラル型にしています。
そしてこのRequest
を返す関数req()
があり、それを呼び戻り値を定数res
で受けたとすると次のようなことができます。
typescript
const res: Response = req();if (res.success) {// res.response ...} else {// res.error ...}
typescript
const res: Response = req();if (res.success) {// res.response ...} else {// res.error ...}
if
の条件がtrue
になる、つまりres.success
がtrue
になるとそのブロックではres.response
を呼び出せます。一方else
のブロックではres.error
を呼び出せます。これはres.success
がtrue
の場合はSuccessResponse
であることが確定しfalse
の場合はErrorResponse
であることが確定するからです。
値があるかもしれないしないかもしれないことを意味するモナドのOptional
をユニオン型を使って表現するとこのようになるでしょう。
typescript
type Some<T> = {present: true;value: T;};type None = {present: false;};type Optional<T> = Some<T> | None;
typescript
type Some<T> = {present: true;value: T;};type None = {present: false;};type Optional<T> = Some<T> | None;
リテラル型を使えばtrue, false
に限らず他の型でも可能です。
typescript
type English = {iso639: "en";thanks: "thank you very much";};type French = {iso639: "fr";merci: "merci beaucoup";};type German = {iso639: "de";danke: "danke schön";};type Langauge = English | French | German;const lang: Langauge = select();switch (lang.iso639) {case "en":return lang.thanks;case "fr":return lang.merci;case "de":return lang.danke;}
typescript
type English = {iso639: "en";thanks: "thank you very much";};type French = {iso639: "fr";merci: "merci beaucoup";};type German = {iso639: "de";danke: "danke schön";};type Langauge = English | French | German;const lang: Langauge = select();switch (lang.iso639) {case "en":return lang.thanks;case "fr":return lang.merci;case "de":return lang.danke;}
上記例ではlang.iso639
がそれに該当します。
リテラル型でなくても他の型どうしであればTypeScriptはこの判別を自動的にしてくれます。
typescript
type Measurement = {b: number;w: number;h: number;};type TopSecret = {b: "secret";w: "secret";h: "secret";};type ThreeSize = Measurement | TopSecret;const size: ThreeSize = measure();if (size.b === "secret") {console.log(size.w);// -> 'secret'console.log(size.h);// -> 'secret'}
typescript
type Measurement = {b: number;w: number;h: number;};type TopSecret = {b: "secret";w: "secret";h: "secret";};type ThreeSize = Measurement | TopSecret;const size: ThreeSize = measure();if (size.b === "secret") {console.log(size.w);// -> 'secret'console.log(size.h);// -> 'secret'}
スリーサイズを公表したくない人は'secret'
という文字をどこかひとつでもに入れておけばTopSecret
型であると判別され、対応するif
ブロックではすべてのサイズは'secret'
になります。