TypeScriptのis演算子(Type Predicates)で作るカスタム型ガード
TypeScript Frontend TypeSystem
結論
val is TargetType でカスタム型ガードを作成し、内部でプロパティの存在・型を厳格検証します。
// TypeScript公式仕様:ユーザー定義型ガード (Type Predicates)
type User = { id: string; name: string };
function isUser(val: unknown): val is User {
return (
typeof val === 'object' &&
val !== null &&
'id' in val &&
typeof (val as Record<string, unknown>).id === 'string' &&
'name' in val &&
typeof (val as Record<string, unknown>).name === 'string'
);
}
TypeScript公式仕様:is 演算子と Narrowing の関係
TypeScript(Narrowing)ドキュメントで定義されている Type Predicates(型述語)は、関数の戻り値の型として parameterName is Type という特殊な構文を指定することで、その関数が true を返した if 分岐スコープ内で対象変数を絞り込む(Narrowing)機能です。
実際に起こる事故:偽型ガードによる本番ランタイムクラッシュ
TypeScriptコンパイラは、val is User と宣言された関数の中身(return 条件文)が本当に正しいかどうかを検証しません。
以下のような雑な型ガードを書いてしまうと、型システムに大きな穴(Soundness Hole)が空き、重度のバグを引き起こします。
// ❌ 危険:判定が不完全な偽型ガード
function isUserUnsafe(val: any): val is User {
return typeof val === 'object'; // null の判定漏れ、id/name の型チェック未実施
}
const input: unknown = null;
if (isUserUnsafe(input)) {
// コンパイラは input を User 型だと信用してしまう!
console.log(input.name.toUpperCase()); // ❌ 本番で TypeError: Cannot read properties of null クラッシュ
}
堅牢な型ガード作成の手順
- 引数の型を
anyではなくunknownに指定する - 内部判定で①
typeof val === 'object'、②val !== nullの両方を必須チェックする in演算子またはObject.prototype.hasOwnPropertyを使用して必要なキーの存在とデータ型をすべて確認する- より複雑なスキーマの場合は、手動
isガードではなく Zod(schema.safeParse)などのバリデーションライブラリへの移行を検討する