判別可能なユニオン (discriminated union)
次のようなタイプエイリアスのSuccessResponse, ErrorResponseを考え、そのユニオン型としてResponseを考えます。
typescripttype SuccessResponse = {success: true;response: Data;};type ErrorResponse = {success: false;error: Error;};type Response = SuccessResponse | ErrorResponse;
typescripttype SuccessResponse = {success: true;response: Data;};type ErrorResponse = {success: false;error: Error;};type Response = SuccessResponse | ErrorResponse;
ユニオン型のResponseはふたつのタイプエイリアスが持つsuccessを共通のプロパティとして持ちますが片方はtrueでもう片方はfalseです。ここはboolean型とせずあえてリテラル型にしています。
そしてこのRequestを返す関数req()があり、それを呼び戻り値を定数resで受けたとすると次のようなことができます。
typescriptconst res: Response = req();if (res.success) {// res.response ...} else {// res.error ...}
typescriptconst 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をユニオン型を使って表現するとこのようになるでしょう。
typescripttype Some<T> = {present: true;value: T;};type None = {present: false;};type Optional<T> = Some<T> | None;
typescripttype Some<T> = {present: true;value: T;};type None = {present: false;};type Optional<T> = Some<T> | None;
リテラル型を使えばtrue, falseに限らず他の型でも可能です。
typescripttype 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;}
typescripttype 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はこの判別を自動的にしてくれます。
typescripttype 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'}
typescripttype 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'になります。