より安全で保守性の高い TypeScript コードを書く
TypeScript で型をユニオン型と照合する方法
しかし、プログラムを実行中に、ある値が特定のユニオン型に属するかどうかを確認したい場合があります。このような場合、型ガードと呼ばれる機能を使用することができます。
型ガードとは
型ガードは、式の結果に基づいて変数の型を絞り込むための仕組みです。具体的には、typeof 演算子、instanceof 演算子、in 演算子、=== 演算子などの条件式を使用して、変数の型が特定の型であるかどうかを判定することができます。
ユニオン型との照合
型ガードを用いて、変数の型をユニオン型と照合するには、以下のいずれかの方法を使用することができます。
typeof 演算子
typeof 演算子は、オペランドの型を文字列として返します。この結果をユニオン型の型リテラルと比較することで、変数の型が特定の型であるかどうかを判定することができます。
function isNumberOrString(value: number | string): boolean {
return typeof value === 'number' || typeof value === 'string';
}
上記の例では、isNumberOrString
関数は、引数が number
型または string
型であるかどうかを判定します。
instanceof 演算子
instanceof 演算子は、オペランドが特定のクラスのインスタンスかどうかを判定します。この演算子を使用して、変数が特定の型のオブジェクトであるかどうかを判定することができます。
class Person {
name: string;
age: number;
}
class Product {
name: string;
price: number;
}
function isPersonOrProduct(value: Person | Product): value is Person {
return value instanceof Person;
}
上記の例では、isPersonOrProduct
関数は、引数が Person
クラスまたは Product
クラスのインスタンスであるかどうかを判定します。isPersonOrProduct
関数が true
を返した場合、value
変数は Person
型として扱うことができます。
in 演算子
interface Animal {
name: string;
}
interface Dog extends Animal {
breed: string;
}
function isDog(animal: Animal): animal is Dog {
return 'breed' in animal;
}
上記の例では、isDog
関数は、引数が Animal
インターフェースを実装しているオブジェクトであり、かつ breed
プロパティを持つかどうかを判定します。isDog
関数が true
を返した場合、animal
変数は Dog
型として扱うことができます。
=== 演算子
=== 演산子は、オペランドが厳密に等しいかどうかを判定します。この演算子を使用して、変数が特定のリテラル値であるかどうかを判定することができます。
function isSmallNumber(value: number): boolean {
return value === 0 || value === 1;
}
上記の例では、isSmallNumber
関数は、引数が 0 または 1 であるかどうかを判定します。
型ガードは、TypeScript において変数の型をより詳細に制御するための強力なツールです。ユニオン型との照合は、型ガードの重要なユースケースの一つであり、プログラムの型安全性と保守性を向上させるのに役立ちます。
上記の例以外にも、型ガードには様々な使用方法があります。詳細は、TypeScript の公式ドキュメントを参照してください。
// ユニオン型を定義
type UserOrAdmin = {
name: string;
age: number;
} | {
name: string;
role: string;
};
// 関数を定義
function isUser(userOrAdmin: UserOrAdmin): userOrAdmin is { name: string; age: number } {
return !('role' in userOrAdmin); // "role" プロパティがない場合は User 型とみなす
}
function isAdmin(userOrAdmin: UserOrAdmin): userOrAdmin is { name: string; role: string } {
return 'role' in userOrAdmin; // "role" プロパティがある場合は Admin 型とみなす
}
// 関数を使用
const user: UserOrAdmin = { name: 'John Doe', age: 30 };
const admin: UserOrAdmin = { name: 'Jane Doe', role: 'admin' };
if (isUser(user)) {
console.log('User:', user.name, user.age);
} else if (isAdmin(admin)) {
console.log('Admin:', admin.name, admin.role);
} else {
console.error('Invalid user or admin object');
}
このコードでは、まず UserOrAdmin
という名前のユニオン型を定義します。この型は、name
と age
プロパティを持つオブジェクト (User
型) または name
と role
プロパティを持つオブジェクト (Admin
型) のいずれかを表すことができます。
次に、isUser
と isAdmin
という 2 つの関数を作成します。これらの関数は、引数が UserOrAdmin
型であるかどうかを判定し、その結果に基づいて User
型または Admin
型として扱うことができます。
最後に、user
と admin
という 2 つの変数を作成し、それぞれ User
型と Admin
型のオブジェクトを代入します。その後、isUser
と isAdmin
関数を使用して、これらの変数の型を判定し、対応する処理を実行します。
型アサーションは、変数に特定の型を明示的に割り当てるための構文です。型ガードと同様に、ユニオン型の型を絞り込むために使用することができます。
function isSmallNumber(value: number): number {
return value as number; // value を number 型として扱う
}
const smallNumber: number = isSmallNumber(10);
上記の例では、isSmallNumber
関数は引数を number
型として扱い、その結果を smallNumber
変数に代入します。
関数シグネチャ
関数シグネチャは、関数の引数と戻り値の型を定義するための構文です。型ガードと同様に、ユニオン型の型を絞り込むために使用することができます。
function greet(person: UserOrAdmin): string {
if (isUser(person)) {
return `Hello, ${person.name}!`;
} else {
return `Welcome, Admin ${person.name}!`;
}
}
上記の例では、greet
関数のシグネチャは、引数が UserOrAdmin
型であることを示しています。また、isUser
型ガードを使用して、引数が User
型であるかどうかを判定し、それに応じてメッセージを返します。
ジェネリック型
ジェネリック型は、型パラメータを使用して、さまざまな型の値を扱うことができる型を定義するための構文です。型ガードと同様に、ユニオン型の型を絞り込むために使用することができます。
interface Box<T> {
value: T;
}
function getBoxValue<T>(box: Box<T>): T {
return box.value;
}
const userBox: Box<UserOrAdmin> = { value: { name: 'John Doe', age: 30 } };
const adminBox: Box<UserOrAdmin> = { value: { name: 'Jane Doe', role: 'admin' } };
console.log('User:', getBoxValue(userBox)); // { name: 'John Doe', age: 30 }
console.log('Admin:', getBoxValue(adminBox)); // { name: 'Jane Doe', role: 'admin' }
上記の例では、Box
という名前のジェネリック型を定義します。この型は、value
プロパティを持つオブジェクトを表しますが、そのプロパティの型は型パラメータ T
で指定されます。getBoxValue
関数は、Box
型の引数を受け取り、その value
プロパティの値を返します。
この例では、userBox
と adminBox
という 2 つの変数を作成し、それぞれ UserOrAdmin
型の値を持つ Box
オブジェクトを代入します。その後、getBoxValue
関数を使用して、これらのオブジェクトの value
プロパティの値を取得します。
ディスクリミネーション型
ディスクリミネーション型は、特定のプロパティに基づいて複数の型を区別するための特殊な種類のユニオン型です。型ガードと同様に、ユニオン型の型を絞り込むために使用することができます。
interface User {
name: string;
age: number;
type: 'user';
}
interface Admin {
name: string;
role: string;
type: 'admin';
}
type UserOrAdmin = User | Admin;
function isAdmin(userOrAdmin: UserOrAdmin): userOrAdmin is Admin {
return userOrAdmin.type === 'admin';
}
// ... (使用方法は前の例と同じ)
上記の例では、User
と Admin
という 2 つのインターフェースを定義します。これらのインターフェースは、name
、age
、role
などのプロパティに加えて、type
というプロパティも持ちます。type
プロパティは、オブジェクトが User
型 (type: 'user'
) または Admin
型 (type: 'admin'
) であることを示すために使用されます。
isAdmin
関数は、引数が Admin
型であるかどうかを判定するために、type
プロパティを使用します。
typescript