目次
JavaScriptのin演算子について
JavaScriptのinを利用することでオブジェクトのプロパティの有無を判定できます。
const car = { make: 'Honda', model: 'Accord', year: 1998 };
console.log('make' in car);
// true
console.log('type' in car);
// false
inを利用した型の絞り込みの実装方法
『オブジェクトのプロパティの有無を判定できる』という機能を活用するとin
で型の絞り込みができます。
以下ではin
を利用した型の絞り込みの実装方法について紹介します。利用するTypeScriptのバージョンは4.3.5
です。
今回利用するサンプルコード
Cat型とDuck型を用意します。鳴き声を表すプロパティがCat型ではmeow
、Duck型ではquack
で定義されています。
また、Cat型とDuck型のUnion型をAnimal型とします。
type Cat = {
name: string;
meow: string;
}
type Duck = {
name: string;
quack: string;
}
type Animal = Cat | Duck;
Cat型であればmeow
、Duck型であればquack
を取得するsay
メソッドを実装する場合を考えます。
ダメな例: 型ガードにtypeofを利用する
typeof
はプリミティブ型の型を判別する演算子です。
typeof
はstring型やnumber型といったプリミティブ型に対しては利用できますが、TypeScriptで独自定義した型には適用できません。
以下のようにCat型かDuck型かを絞り込みたい場合にtypeof
を利用すると'Cat' only refers to a type, but is being used as a value here.
というエラーが出てしまいます。
const say = (animal: Animal): string => {
if (typeof animal === Cat) { // 'Cat' only refers to a type, but is being used as a value here.
return animal.meow // Animal型と推論されるためエラー
} else {
return animal.quack // Animal型と推論されるためエラー
}
}
なお、typeofの詳細解説は【TypeScript】型ガード(タイプガード)の概要。typeofとinstanceofの利用例で紹介しています。
正しい方法: inを利用して型を特定する
inを利用して型に定義されているプロパティの有無をチェックすることで型の判別ができます。
今回の場合ですと、Cat型にのみ存在するmeow
の有無をチェックすることでブロック内の変数の型を絞り込めます。
const say = (animal: Animal): string => {
if ('meow' in animal) {
return animal.meow // Cat型と推論される
} else {
return animal.quack // Duck型と推論される
}
}
const tama: Cat = {
name: 'Tama',
meow: "meow!!meow!!"
}
const shiro: Duck = {
name: 'Shiro',
quack: "quack!!quack!!"
}
console.log(say(tama))
// "meow!!meow!!"
console.log(say(shiro))
// "quack!!quack!!"
参考: プロパティの値で型を判断する場合、型の絞り込みはできない
Union型のメンバー(ここでいうCat型とDuck型)それぞれに対して、型の種類を表すプロパティを追加することで型の判別ができます。
たとえばkind
というプロパティを追加し、Cat型であればcat
、Duck型であればduck
の文字列をセットします。これによりkind
の値を手がかりに型の判別ができます。
ただしin
を利用した時と違い、ブロック内の変数に対する型推論はAnimal型のままです。ですので、型アサーション(as
)を利用して変数の型をキャストする必要があります。
type Cat = {
kind: string; // 新しく追加
name: string;
meow: string;
}
type Duck = {
kind: string; // 新しく追加
name: string;
quack: string;
}
type Animal = Cat | Duck;
const say = (animal: Animal): string => {
if (animal.kind === 'cat') {
return (animal as Cat).meow // Animal型と推論されるため、Cat型にキャストする。(キャストしないとDuck型はmeowを持たないとエラーで指摘される)
} else {
return (animal as Duck).quack // Animal型と推論されるため、Duck型にキャストする(キャストしないとCat型はquackを持たないとエラーで指摘される)
}
}
const tama: Cat = {
kind: 'cat',
name: 'Tama',
meow: "meow!!meow!!"
}
const shiro: Duck = {
kind: 'duck',
name: 'Shiro',
quack: "quack!!quack!!"
}
console.log(say(tama))
// "meow!!meow!!"
console.log(say(shiro))
// "quack!!quack!!"
なお、型アサーションの詳細解説はTypeScriptのas(型アサーション)の概要と使いどころで紹介しています。
さいごに
Twitter(@nishina555)やってます。フォローしてもらえるとうれしいです!