Type Guard(型ガード)の色々な例
TypeScript
Type Guard(型ガード)とは
型の絞り込み。
型が複数考えられる場合にType Guardで型を特定していくこと。
tyepofを使ったType Guard
以下のように、typeof用いることで型の絞り込みが出来る。
export const foo = (value: string | number | boolean) => {
if(typeof value === "string") {
return value;
// value: string
}
if(typeof value === "number") {
return value;
// value: number
}
return value;
// value: boolean
};
JavaScriptや演算子を使ったType Guard
JavaScriptを使ったType Guard
Array.isArray()のようなJavaScriptのメソッドを用いて、型の絞り込みが出来る。
export const foo = (value: string | string[]) => {
if(Array.isArray(value)) {
return value;
// value: string[]
}
return value;
// value: string
};
演算子を使ったType Guard
例えば以下のように論理否定演算子を用いて、valueがfalsyだった場合にif文の中を返すことで、
undefinedの可能性を消去できる。
export const foo = (value?: string) => {
if(!value) {
return value;
// value: string | undefined
}
return value;
// value: string
};
こちらは頻出で、React等を使っていて上からpropsが渡ってきた時にpropsの値がオプショナルだった場合に可能性を消去する。そして、下のreturn文でコンポーネントを記述する。のような場面で出てくる。
ちなみに、if文内はvalue: string | undefinedとなっているが、
空文字に!を付けるとtruthyとなる=条件分岐に入りstringの可能性が残る。ということ。
実際は以下のように、nullやただのreturnにする。
export const foo = (value?: string) => {
if(!value) {
return null;
}
return value;
};
or
export const foo = (value?: string) => {
if(!value) {
return;
}
return value;
};
in演算子を使ったType Guard
以下のように"nickname" in valueとすることで、valueにnicknameがあるかどうかで、
型の絞り込みが出来る。
type UserA = { name: string };
type UserB = { name: string; nickname: string };
export const foo = (value: UserA | UserB) => {
if("nickname" in value) {
return value;
// value: UserB
}
return value;
// value: UserA
};
タグ付きUnion Types(Discriminated Union,Taggrd Union)を使ったType Guard
以下のようにUserAもUserBも同じようなプロパティを持っている時に、
タグ付きUnion Typesを用いて型の絞り込みが出来る。
type UserA = { name: string; lang: "ja" };
type UserB = { name: string; lang: "en" };
export const foo = (value: UserA | UserB) => {
if(value.lang === "ja") {
return value;
// value: UserA
}
return value;
// value: UserB
};
実際にはAdminと一般ユーザーを分けたりするようなtype,kind,id等をよく見るが、特に決まりはなくいろんなプロパティがTagとして使われる。
switch文を使ったType Guard
以下のように、switch文を用いて型の絞り込みが出来る。
type UserA = { name: string; lang: "ja" };
type UserB = { name: string; lang: "en" };
type UserC = { name: string; lang: "fr" };
export const foo = (value: UserA | UserB | UserC) => {
switch (value.lang) {
case "ja": {
return value;
}
case "en": {
return value;
}
case "fr": {
return value;
}
default: {
throw Error("lang is not defined!");
}
}
};