O'Reilly のプログラミングTypeScriptを読んでいると、
「ユーザー定義型ガード」なるものが出てきました。
今まで使用したことがなく気になったため、
今回はこのユーザー定義型ガードについてまとめます。
ユーザー定義型ガードとは
ユーザー定義型ガードは、開発者が独自に定義できる型チェックの仕組みで、
特定の条件下で変数の型を具体的に絞り込むことができます。
以下の形式で定義します。
function isType(arg: any): arg is SpecificType {
// 型チェックのロジック
return // boolean
}
arg is SpecificType
という個所が重要で、
この関数が true
を返した場合、arg
は SpecificType
として扱われます。
使用例
ここではユーザー定義型ガードの使用例をみてみましょう。
ユーザー権限判断
この例ではユーザーの権限が admin
guest
どちらなのかを判断しています。
interface Admin {
role: 'admin';
privileges: string[];
}
interface Guest {
role: 'guest';
}
type User = Admin | Guest;
function isAdmin(user: User): user is Admin {
return user.role === 'admin';
}
const user: User = { role: 'admin', privileges: ['manage-users'] };
if (isAdmin(user)) {
console.log(user.privileges); // Admin型のプロパティにアクセス可能
} else {
console.log('User is a guest');
}
通常の型ガードとの比較
通常の型ガードとも比較してみましょう
下記は、型 Circle
、Rectangle
を判断する例です。
interface Shape {
type: string;
}
interface Circle extends Shape {
type: 'circle';
radius: number;
}
interface Rectangle extends Shape {
type: 'rectangle';
width: number;
height: number;
}
通常の型ガード
typeof
を使用した場合
typeof
はプリミティブ型にしか対応していないため、具体的な型を絞り込むことはできません。
const isCircle = (shape: Shape): boolean => {
if (typeof shape === 'circle') {
// エラー: `typeof`では"circle"のようなカスタム型は判定できない
return true;
}
return false;
};
instanceof
を使用した場合
instanceof
はクラスインスタンスを判定するものですが、ここでは interface
を使用しているため、instanceof
は適用できません。
const isCircle = (shape: Shape): boolean => {
if (shape instanceof Circle) {
// エラー: `Circle`はインターフェースのため `instanceof`は使用できない
return true;
}
return false;
};
ユーザー定義型ガード
以下のようにCircle
と Rectangle
を判定する関数を作成できます。
function isCircle(shape: Shape): shape is Circle {
return shape.type === 'circle' && 'radius' in shape && typeof shape.radius === 'number';
}
function isRectangle(shape: Shape): shape is Rectangle {
return shape.type === 'rectangle' && 'width' in shape && 'height' in shape;
}
const printShapeInfo = (shape: Shape) => {
if (isCircle(shape)) {
console.log(`Circle with radius: ${shape.radius}`);
} else if (isRectangle(shape)) {
console.log(`Rectangle with width: ${shape.width}, height: ${shape.height}`);
} else {
console.log('Unknown shape');
}
};
const circle: Circle = { type: 'circle', radius: 5 };
const rectangle: Rectangle = { type: 'rectangle', width: 10, height: 20 };
printShapeInfo(circle); // Circle with radius: 5
printShapeInfo(rectangle); // Rectangle with width: 10, height: 20
おわりに
typeof
や instanceof
では、基本型の判定しかできません。
ユーザー定義型ガード を使用することで、type
やその他のプロパティの条件を基に型を判定でき、非常に便利だなと思いました。
それでは。
参考文献