目次
ユーザー定義型ガードとis(Type Predicate)について
TypeScriptには型を絞り込む『型ガード』と呼ばれる機能があります。
前回、【TypeScript】型ガード(タイプガード)の概要。typeofとinstanceofの利用例でtypeof
・instanceof
を利用した型ガードの実装方法について紹介しました。
型ガードを実現するにはtypeof
やinstanceof
といった演算子を利用する方法以外に、関数で定義する方法があります。
型ガードを関数で定義する場合は、関数の戻り値を引数 is 型
とします。
戻り値が引数 is T
の関数は「trueなら引数は型T、falseなら型Tではない」という挙動になります。
『引数 is 型』のことを型述語 (Type Predicate) と呼びます。
また、戻り値が『引数 is 型』で定義された型ガードの役割をはたす関数のことをユーザー定義型ガードと呼びます。
ユーザー定義型ガードの実装方法
以下ではユーザー定義型ガードの実装方法について紹介します。利用するTypeScriptのバージョンは4.3.5
です。
今回利用するサンプルコード
今回は「文字列であれば大文字に変換、数値であれば数値を文字列にして返す」という関数を具体例として利用します。
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"
typeof
による型ガードをユーザー定義型ガードに書き直します。
ダメな方法: 戻り値をbooleanにする
型ガードの箇所であるtypeof value === "string"
の部分を取り出し、isString
というユーザー定義型ガードを実装しています。
一見問題なさそうですが、typeofによる型の絞り込みが適用されるのはisStringメソッド内だけですので、isStringは型ガードとして機能しません。
その結果、convert
メソッド内で型を絞り込めずコンパイルエラーになります。
const isString = (value: string | number): boolean => {
return typeof value === "string";
}
const convert = (value: string | number): string => {
if (isString(value)) {
return value.toUpperCase(); // string | number 型と推論されるためエラー
} else {
return value.toString();
}
}
正しい方法: 戻り値をis(Type Predicate)にする
戻り値をis(Type Predicate)にすることで型ガードとして機能するメソッド(ユーザー定義型ガード)になります。
const isString = (value: string | number): value is string => {
return typeof value === "string";
}
const convert = (value: string | number): string => {
if (isString(value)) {
return value.toUpperCase(); // string型と推論される
} else {
return value.toString(); // number型と推論される
}
}
console.log(convert('hoge'))
// "HOGE"
console.log(convert(123))
// "123"
参考: オブジェクトのユーザー定義型ガード
オブジェクトの型は、例えばオブジェクトのプロパティの有無をin
で確認することで判定できます。
以下はオブジェクトを判定するユーザー定義型ガードの例です。
type CatType = {
name: string
meow: string
}
type DuckType = {
name: string
quack: string
}
type AnimalType = CatType | DuckType
// trueならCatType型と推定される
const isCatType = (animal: AnimalType): animal is CatType =>
// nameプロパティとmeowプロパティを持つオブジェクトはCatType型
'name' in animal && 'meow' in animal
const say = (animal: AnimalType): void => {
if (isCatType(animal)) {
console.log(animal.meow)
} else {
console.log(animal.quack)
}
}
const tama: AnimalType = { name: 'Tama', meow: 'meow!!meow!!' }
const ahiru: AnimalType = { name: 'Ahiru', quack: 'quack!!quack!!') }
say(tama) // meow!!meow!!
say(ahiru) // quack!!quack!!
オブジェクトの型ガードの詳細解説は【TypeScript】typeof、inを利用したオブジェクトの型判定・型ガード(Type Guard)の方法で紹介しています。
さいごに
Twitter(@nishina555)やってます。フォローしてもらえるとうれしいです!