【TypeScript】カスタムタイプガードの実装パターン - 型安全な条件分岐とエラーチェック
2024-11-10
2024-11-10
TypeScriptのカスタムタイプガードとは
TypeScript
のカスタムタイプガードは、特定の条件に基づいて値が特定の型であることを判定するための関数です。これにより、コードの安全性と可読性が向上し、型安全なデータ処理が可能になります。特に、APIから受け取ったデータが期待する型かどうかを判定する際に活用できます。
カスタムタイプガードは、TypeScript
の型推論をさらに強力にするため、さまざまな場面で有効です。ここでは、カスタムタイプガードの基本から実践的な使い方までを解説します。
カスタムタイプガードの基本構文
カスタムタイプガードの関数は、返り値にvalue is 型
という構文を使用します。この返り値の形式によって、TypeScript
は条件が満たされた場合に特定の型を推論できます。
function isString(value: any): value is string {
return typeof value === "string";
}
上記の例では、isString
関数がvalue
が文字列型かどうかを判定しています。この関数がtrue
を返した場合、TypeScript
はvalue
がstring
型であると認識します。
カスタムタイプガードの実践例
例1: ユニオン型での条件分岐
例えば、ユニオン型string | number
の変数に対して、数値である場合のみ特定の処理を行いたいとします。この場合、カスタムタイプガードを使って型を判別できます。
function isNumber(value: any): value is number {
return typeof value === "number";
}
function processValue(value: string | number) {
if (isNumber(value)) {
// valueはnumber型とみなされる
console.log(`Number: ${value.toFixed(2)}`);
} else {
// valueはstring型とみなされる
console.log(`String: ${value.toUpperCase()}`);
}
}
processValue(42); // 出力: Number: 42.00
processValue("hello"); // 出力: String: HELLO
isNumber
関数がtrue
を返すと、value
はnumber
型として認識され、number
型固有のメソッドtoFixed
が使用可能になります。
例2: オブジェクトの特定プロパティの存在を確認
特定のプロパティを持つかどうかで型を判断したい場合にも、カスタムタイプガードを使います。以下の例では、AdminUser
型とGuestUser
型を区別するために、isAdminUser
関数を使用しています。
interface AdminUser {
role: "admin";
privileges: string[];
}
interface GuestUser {
role: "guest";
}
type User = AdminUser | GuestUser;
function isAdminUser(user: User): user is AdminUser {
return "privileges" in user;
}
function showPrivileges(user: User) {
if (isAdminUser(user)) {
console.log(`Admin privileges: ${user.privileges.join(", ")}`);
} else {
console.log("No privileges for guest user.");
}
}
const admin: User = { role: "admin", privileges: ["manage-users", "edit-settings"] };
const guest: User = { role: "guest" };
showPrivileges(admin); // 出力: Admin privileges: manage-users, edit-settings
showPrivileges(guest); // 出力: No privileges for guest user.
この例では、isAdminUser
関数によってprivileges
プロパティの有無を判定し、AdminUser
型かどうかを確認しています。
複雑なカスタムタイプガードの実装パターン
例3: APIレスポンスの検証
外部APIのレスポンスは必ずしも型が保証されていないため、カスタムタイプガードを使用して型安全に処理できます。例えば、APIレスポンスがSuccessResponse
型かErrorResponse
型かを判定する関数を作成します。
interface SuccessResponse {
status: "success";
data: any;
}
interface ErrorResponse {
status: "error";
message: string;
}
type ApiResponse = SuccessResponse | ErrorResponse;
function isSuccessResponse(response: ApiResponse): response is SuccessResponse {
return response.status === "success";
}
function handleResponse(response: ApiResponse) {
if (isSuccessResponse(response)) {
console.log("Data:", response.data);
} else {
console.error("Error:", response.message);
}
}
// 使用例
const response1: ApiResponse = { status: "success", data: { id: 1, name: "Alice" } };
const response2: ApiResponse = { status: "error", message: "Not found" };
handleResponse(response1); // 出力: Data: { id: 1, name: "Alice" }
handleResponse(response2); // 出力: Error: Not found
ここでは、status
プロパティの値でレスポンスを判定し、SuccessResponse
かErrorResponse
かに応じて異なる処理を行っています。
例4: ネストされたオブジェクトの型チェック
カスタムタイプガードを使えば、ネストされたオブジェクトのプロパティに対する型チェックも行えます。以下の例では、ネストされたAddress
プロパティを持つかどうかで型を判定しています。
interface UserWithAddress {
name: string;
address: {
city: string;
zipCode: string;
};
}
interface UserWithoutAddress {
name: string;
}
type User = UserWithAddress | UserWithoutAddress;
function hasAddress(user: User): user is UserWithAddress {
return "address" in user && typeof user.address === "object";
}
function showUserAddress(user: User) {
if (hasAddress(user)) {
console.log(`City: ${user.address.city}, Zip: ${user.address.zipCode}`);
} else {
console.log("No address available.");
}
}
const user1: User = { name: "Alice", address: { city: "New York", zipCode: "10001" } };
const user2: User = { name: "Bob" };
showUserAddress(user1); // 出力: City: New York, Zip: 10001
showUserAddress(user2); // 出力: No address available.
この例では、address
プロパティが存在し、かつその型がobject
であることを確認することで、安全に型を判定しています。
カスタムタイプガードを使う際の注意点
カスタムタイプガードは便利ですが、いくつか注意が必要です。
- 型の一貫性: カスタムタイプガード関数が返す型情報は
TypeScript
全体で一貫性を保つため、型定義の変更や関数のロジックを変更した場合は、すべての関連箇所を再確認することが重要です。 - 複雑な条件は避ける : カスタムタイプガード内で条件を複雑にしすぎると、型推論が混乱し、かえって保守性が低下します。できる限りシンプルな条件を心がけましょう。
- ユーザー定義型と外部データの整合性: 外部APIのデータを検証する場合、カスタムタイプガードがしっかりとデータの整合性を確保しているかを確認する必要があります。
まとめ
TypeScript
のカスタムタイプガードを活用することで、型の安全性を保ちながら、条件に応じた柔軟なデータ処理が可能になります。特に、ユニオン型やAPIレスポンスの判定など、型が確定していないデータを扱う場面で大きな効果を発揮します。これらの技術を活用して、より堅牢で型安全なTypeScript
コードを実現しましょう。