目次
型ガード(Type Guard)について
型ガード(Type Guard)とはif文やcase文をはじめとした条件分岐で変数の型を判別し、ブロック内の変数の型を絞り込む機能のことをいいます。
型ガードを利用するとブロック内の変数の型が特定されるため、型のキャスト(型を明示して変換すること)が不要になります。
複数の型を持つ可能性のあるUnion型の変数に対して「この型の場合はこの処理をする」といったことを実現したい場合に型ガードが利用されます。
型の判別する演算子にはtypeof
とinstanceof
があります。typeof
はプリミティブ型に、instanceof
はクラスに対して使います。
型ガードの具体的な利用例
型ガードの利用例について紹介します。利用するTypeScriptのバージョンは4.3.5
です。
typeofを利用した型ガード
「文字列であれば大文字に変換、数値であれば数値を文字列にして返す」という関数を作成するとします。
以下のコードは文字列の長さを条件分岐に利用して実装した例です。
この場合、value.length
やvalue.toUpperCase()
を実行する際のvalue
がnumber型のケースもあるためコンパイルエラーになってしまいます。
const convert = (value: string | number): string => {
if (value.length > 0) { // string | number 型と推論されるためエラー
return value.toUpperCase(); // string | number 型と推論されるためエラー
} else {
return value.toString();
}
}
上記のメソッドをtypeof
による型ガードを利用して実装した例が以下になります。
typeof
によってvalue
の型が特定されるため意図した挙動になります。
const convert = (value: string | number): string => {
if (typeof value === 'string') {
return value.toUpperCase(); // string型と推論される
} else {
return value.toString(); // number型と推論される
}
}
console.log(convert('hoge'))
// "HOGE"
console.log(convert(123))
// "123"
instanceofを利用した型ガード
CatクラスとDuckクラスがあったとします。
鳴き声を表すプロパティがCatクラスではmeow
、Duckクラスではquack
で定義されています。
class Cat {
name: string
meow: string
constructor(name: string, meow: string) {
this.name = name
this.meow = meow
}
}
class Duck {
name: string
quack: string
constructor(name: string, quack: string) {
this.name = name
this.quack = quack
}
}
Catクラスであればmeow
、Duckクラスであればquack
を取得するsay
メソッドを実装するとします。
以下のコードはmeow
プロパティの有無を条件分岐に利用して実装した例です。
この場合、animal
をCatクラスかDuckクラスか特定できないため、animal.meow
やanimal.quack
でコンパイルエラーが発生します。
const say = (animal: Cat | Duck): string => {
if (animal.meow !== undefined) { // Cat | Duck と推論されるためエラー
return animal.meow // Cat | Duck と推論されるためエラー
} else {
return animal.quack // Cat | Duck と推論されるためエラー
}
}
上記のメソッドをinstanceof
による型ガードを利用して実装した例が以下になります。
instanceof
によってanimal
のクラスが特定されるため意図した挙動になります。
const say = (animal: Cat | Duck): string => {
if (animal instanceof Cat) {
return animal.meow // Catクラスと推定される
} else {
return animal.quack // Duckクラスと推定される
}
}
const tama = new Cat('Tama', 'meow!!meow!!')
const shiro = new Duck('Shiro', 'quack!!quack!!')
console.log(say(tama))
// "meow!!meow!!"
console.log(say(shiro))
// "quack!!quack!!"
参考: nullableな変数(nullになり得る変数)を扱う場合の型の絞り込み
「文字列であれば大文字に変換、nullであれば空文字を返す」という関数を作成するとします。
nullableな変数の型の絞り込みはtypeof
を利用せずともnullチェックで実現できます。
nullチェックによる型の絞り込み例は以下の通りです。
const getUpperCase = (value: string | null): string => {
if (value != null) {
return value.toUpperCase(); // string型と推論される
} else {
return ''; // null型と推論される
}
}
console.log(getUpperCase('hoge'))
// "HOGE"
console.log(getUpperCase(null))
// ""
さらに上記のコードは以下のように簡潔に記述できます。
const getUpperCase = (value: string | null): string => {
return value !=null && value.toUpperCase(); || '';
}
console.log(getUpperCase('hoge'))
// "HOGE"
console.log(getUpperCase(null))
// ""
型ガードの適用範囲について
string | number
というUnion型を具体例に、if文における型ガードの適用範囲について紹介します。
if文で型ガードを利用した場合、if節の中のみ型ガードが適用される
if節の中のみ型ガードが適用され、if文の外は型ガードの適用範囲外になります。
const convert = (value: string | number): string => {
if (typeof value === 'string') {
// string型と推論される
}
// string | number 型と推論される
}
if文で型ガードを利用した場合、else節には型ガードによって必然的に決まる型が適用される
if文でtypeof value === 'string'
とした場合、else節には必然的に決まるnullが変数に適用されます。
const convert = (value: string | number): string => {
if (typeof value === 'string') {
// string型と推論される
} else {
// number型と推論される
}
}
if文で型ガードを利用かつif節でreturnした場合、if文の外には型ガードによって必然的に決まる型が適用される
「if文でtypeof value === 'string'
という条件がある、かつif節でreturn
されている」場合、if文の外は必然的にnullに対する処理になるため、変数はnullとして扱われます。
const convert = (value: string | number): string => {
if (typeof value === 'string') {
// string型と推論される
return value.toUpperCase();
}
// number型と推論される
return "";
}
さいごに
Twitter(@nishina555)やってます。フォローしてもらえるとうれしいです!