TypeScript特有の書き方
function isAdmin(person: Person): person is Admin
このコードを見て「なにこの書き方?」って思ったことあるでしょうか?
実はこれ、TypeScriptの機能のひとつである “型述語(Type Predicate)”。
戻り値の型っぽいが、実は型推論を操るための特殊な構文です。
今回はその正体と、実務でどう使えるかをわかりやすく紹介していきます。
前提
interface User {
type: 'user';
name: string;
occupation: string;
}
interface Admin {
type: 'admin';
name: string;
role: string;
}
type Person = User | Admin;
UserとAdminという二つの状態があり、これら二つを合わせた型をPerson型とします。
まずは普通の関数の場合
function isAdmin(person: Person): boolean {
return person.type === 'admin';
}
if (isAdmin(person)) {
console.log(person.role); // ❌ エラー!
}
この書き方だと isAdmin は「ただのbooleanを返す関数」。TypeScriptは「trueかfalseを返すだけ」としか認識しないため、person.role にアクセスすると「そんなプロパティないよ!」と怒られます。(なぜならpersonがUserかAdminかをTypeScript側で解釈できないため)
そこで登場する「型述語(Type Predicate)」
function isAdmin(person: Person): person is Admin {
return person.type === 'admin';
}
if (isAdmin(person)) {
console.log(person.role); // ✅ OK!
}
person is Admin の部分が型述語。
これは「この関数がtrueを返すとき、person は Admin 型だよ」とTypeScriptの型システムに教えている特別な文法です。
型述語の使いどころ
function isAdmin(p: Person): p is Admin {
return p.type === 'admin';
}
function isUser(p: Person): p is User {
return p.type === 'user';
}
const persons: Person[] = [
{ type: 'user', name: 'Kate', occupation: 'Astronaut' },
{ type: 'admin', name: 'Bruce', role: 'World saver' }
];
persons.filter(isAdmin).forEach(admin => {
console.log(admin.role); // ✅ OK
});
persons.filter(isUser).forEach(user => {
console.log(user.occupation); // ✅ OK
});
filter( ) に直接 isAdmin を渡せるのがポイントですね。
こうすることで、返ってくる配列の型が Admin[ ] に自動で絞り込まれます。
つまり、安全で、かつ可読性の高いコードになります。
まとめ
-
( ): p is T は Type Predicate(型述語) という特殊な構文
-
戻り値の型注釈とは似て非なるもの
-
TypeScriptに「この条件のときはこの型」と教えられる
-
結果、コードが安全で読みやすくなる