はじめに
typescriptで開発をしていると複数の型の可能性があるものを絞り込むことは多々あると思います。
その際に使えるユーザー定義型ガードの使用方法を紹介します。
早速使用例をみてみましょう
使用例
以下のようなコードがあるとします。
一見、コンパイルが通りそうな気配がしますが、これだとエラーになります。
以下のような例だと引数foo
はunknown
型のままです。
const isBird = (animal: unknown): boolean => {
return animal === "bird";
};
const example = (foo: unknown) => {
if (isBird(foo)) {
console.log(foo.length); // Error: Object is of type 'unknown'.
}
};
example('bird')
ではどうすればよいのか?
ここで登場するのがis
です。
const isBird = (animal: unknown): animal is string => {
return animal === "bird";
};
const example = (foo: unknown) => {
if (isBird(foo)) {
console.log(foo.length); // 4
}
};
example('bird')
以下の部分が変わったところですが、引数animalの型をis
を使用して明示的に定義しています。
こうすることで、foo
の型はstring
となり、ちゃんとコンパイルして4
というlengthが取れていることがわかると思います。
const isBird = (animal: unknown): animal is string => {
ではもっと実践的な使い方を見てみましょう。
is
はオブジェクトの型を絞る時にも使用できます。
以下例のようにTeacher
型とStudent
型の2種類型があり、
Teacher型
の時はこういう処理Student型
の時はこういう処理のように条件分岐をさせたいとします。
type Teacher = {
name: string;
age: number;
role: string;
};
type Student = {
name: string;
age: number;
};
案①
typeof
で判定する。
そもそもtypeofはプリミティブな型を判定するので、オブジェクトに対してtypeof使用するとobject
型と認識されてしまいます
const getPersonRole = (person: Teacher | Student) => {
if(typeof person === 'Teacher') { // Error: This condition will always return 'false' since the types '"string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function"' and '"Teacher"' have no overlap.
console.log(person.role)
}
}
案②
if文を使用してプロパティの有無を確かめる
これだとコンパイル時にStudent型にはroleがないのでエラーが出ます。
const getPersonRole = (person: Teacher | Student) => {
if(!!person.role) { // Error: Property 'role' does not exist on type 'Student'.
console.log(person.role)
}
}
案③
is
を使用して型ガードする。
isTeacher()
関数は引数personのroleがundefined
か判定してbooleanを返しています。
person is Teacher
で明示的にpersonはTeacher型であることを示しています。
こうすることで無事personの型で条件分岐することに成功しています。
const isTeacher = (person: Teacher | Student): person is Teacher => {
return (person as Teacher).role !== undefined
}
const getPersonRole = (person: Teacher | Student) => {
if(isTeacher(person)) {
console.log(person.role) // personがTeacer型として推論される
} else {
console.log(person.age) // personがStudent型として推論される
}
}
おわりに
複数の型の可能性がある時に結構使えそうですね。