【初心者向け】TypeScript: ユニオン型とリテラル型プロパティの連携でスマートな型推論を実現
TypeScriptにおけるリテラル型プロパティを用いたユニオン型からの型選択
例題
まず、以下の例題を見てみましょう。
type UserRole = "admin" | "editor" | "guest";
type User = {
id: number;
name: string;
role: UserRole;
};
function isAdmin(user: User): user is { role: "admin" } {
return user.role === "admin";
}
const user1: User = {
id: 1,
name: "Taro",
role: "admin"
};
const user2: User = {
id: 2,
name: "Jiro",
role: "editor"
};
if (isAdmin(user1)) {
console.log(user1.id); // 型 'number' として推論される
} else {
console.log(user1.name); // 型 'string' として推論される
}
この例題では、User
型はid
、name
、role
プロパティを持つオブジェクトを表します。role
プロパティの型は、admin
、editor
、guest
のいずれかのリテラル型です。
isAdmin
関数は、引数として渡されたUser
型のオブジェクトがrole
プロパティに"admin"
を持つかどうかを判定します。判定結果に基づいて、user
オブジェクトの型を具体的に絞り込むことができます。
解説
上記の例題で、isAdmin
関数内で行われている型絞り込みについて詳しく解説します。
type UserRole = "admin" | "editor" | "guest";
type User = {
id: number;
name: string;
role: UserRole;
};
function isAdmin(user: User): user is { role: "admin" } {
return user.role === "admin";
}
const user1: User = {
id: 1,
name: "Taro",
role: "admin"
};
const user2: User = {
id: 2,
name: "Jiro",
role: "editor"
};
if (isAdmin(user1)) {
console.log(user1.id); // 型 'number' として推論される
console.log(user1.name); // 型 'string' として推論される
} else {
console.log(user1.id); // 型 'number' として推論されない
console.log(user1.name); // 型 'string' として推論される
}
console.log(user2.id); // 型 'number' として推論される
console.log(user2.name); // 型 'string' として推論される
console.log(user2.role); // 型 'editor' として推論される
詳細説明
if文内の型推論
isAdmin(user1)
の条件式がtrue
に評価された場合、user1
オブジェクトの型は{ role: "admin" }
に絞り込まれます。- これにより、
console.log(user1.id)
とconsole.log(user1.name)
の型推論結果が、number
型とstring
型になります。 - 一方、条件式が
false
に評価された場合、user1
オブジェクトの型は絞り込まれません。
isAdmin関数の型注釈
- 関数の戻り値の型は、
user is { role: "admin" }
というジェネリック型パラメーターを用いて記述されています。これは、user
オブジェクトがrole
プロパティに"admin"
を持つ場合にのみtrue
を返し、そうでない場合はfalse
を返すことを意味します。 - この型注釈により、
isAdmin
関数内でuser
オブジェクトの型を絞り込むことができるようになります。
- 関数の戻り値の型は、
型ガードを用いる方法
型ガードは、条件式を用いて変数の型を絞り込むための構文です。ユニオン型からの型選択にも活用できます。
type UserRole = "admin" | "editor" | "guest";
type User = {
id: number;
name: string;
role: UserRole;
};
function isAdmin(user: User): user is { role: "admin" } {
if (user.role === "admin") {
return true;
} else {
return false;
}
}
const user1: User = {
id: 1,
name: "Taro",
role: "admin"
};
const user2: User = {
id: 2,
name: "Jiro",
role: "editor"
};
if (isAdmin(user1)) {
console.log(user1.id); // 型 'number' として推論される
console.log(user1.name); // 型 'string' として推論される
} else {
console.log(user1.id); // 型 'number' として推論されない
console.log(user1.name); // 型 'string' として推論されない
}
console.log(user2.id); // 型 'number' として推論される
console.log(user2.name); // 型 'string' として推論される
console.log(user2.role); // 型 'editor' として推論される
この例では、isAdmin
関数を用いてuser
オブジェクトのrole
プロパティが"admin"
かどうかを判定し、型ガードを行っています。
型ガードの結果に基づいて、if
文内のuser1
オブジェクトの型を絞り込むことができます。
in演算子を用いる方法
in
演算子は、オブジェクトのプロパティが存在するかどうかを確認するために用いる演算子です。ユニオン型からの型選択にも活用できます。
type UserRole = "admin" | "editor" | "guest";
type User = {
id: number;
name: string;
role: UserRole;
};
function isAdmin(user: User): user is { role: "admin" } {
return "admin" in user;
}
const user1: User = {
id: 1,
name: "Taro",
role: "admin"
};
const user2: User = {
id: 2,
name: "Jiro",
role: "editor"
};
if (isAdmin(user1)) {
console.log(user1.id); // 型 'number' として推論される
console.log(user1.name); // 型 'string' として推論される
} else {
console.log(user1.id); // 型 'number' として推論されない
console.log(user1.name); // 型 'string' として推論されない
}
console.log(user2.id); // 型 'number' として推論される
console.log(user2.name); // 型 'string' として推論される
console.log(user2.role); // 型 'editor' として推論される
型アサーションを用いる方法
型アサーションは、変数の型を明示的に指定するための構文です。ユニオン型からの型選択にも活用できます。
type UserRole = "admin" | "editor" | "guest";
type User = {
id: number;
name: string;
role: UserRole;
};
function isAdmin(user: User): boolean {
return user.role === "admin";
}
const user1: User = {
id: 1,
name: "Taro",
role: "admin"
};
const user2: User
typescript