タイプガードおよびアサーション
11.4.1. ユーザー定義タイプガード
is キーワードを使用してタイプガード関数を作成します。
typescript
interface AdminUser { id: number; name: string; role: 'admin'; permissions: string[] }
interface GuestUser { id: number; name: string; role: 'guest' }
type AppUser = AdminUser | GuestUser
function isAdminUser(user: AppUser): user is AdminUser {
return user.role === 'admin'
}
function renderDashboard(user: AppUser) {
if (isAdminUser(user)) {
console.log(user.permissions) // AdminUser に絞り込まれる
}
}- タイプガード関数名は
isプレフィックスで始めます。 - 複雑な条件分岐が繰り返される場合、タイプガードとして抽出して再利用します。
11.4.2. as アサーションの使用基準
as タイプアサーションはコンパイラの型検査を迂回するため、使用を最小限にします。
許容する場合:
typescript
const canvas = document.getElementById('chart') as HTMLCanvasElement
const target = (event as MouseEvent).target as HTMLInputElement禁止する場合:
typescript
const data = fetchData() as UserProfile // 根拠のないアサーション
const value = rawData as any as SpecificType // 二重アサーションnon-null アサーション演算子(!)は使用しません。オプショナルチェイニング(?.)または明示的な null チェックで代替します。
typescript
// 誤った例
const name = user!.name
// 正しい例
const name = user?.name11.4.3. unknown vs any
外部から流入するデータは unknown 型で受信した後、タイプガードで絞り込んで使用します。
typescript
interface ErrorResponse { success: false; message: string }
function isErrorResponse(value: unknown): value is ErrorResponse {
return (
typeof value === 'object' &&
value !== null &&
'success' in value &&
(value as Record<string, unknown>).success === false
)
}
async function handleApiResponse(response: Response): Promise<void> {
const body: unknown = await response.json()
if (isErrorResponse(body)) {
throw new Error(body.message)
}
}anyは型検査を無効化しますが、unknownは使用前に型確認を強制します。catchブロックのエラーオブジェクトはunknown型であるため、instanceof検査を実行します。
11.4.4. 型の絞り込みパターン
TypeScript が提供する型の絞り込みメカニズムを状況に応じて使用します。
| パターン | 用途 | 例 |
|---|---|---|
typeof | プリミティブ型の判別 | typeof x === 'string' |
instanceof | クラスインスタンスの判別 | error instanceof TypeError |
in | プロパティの存在確認 | 'email' in user |
| 判別ユニオン | 共通判別プロパティによる分岐 | event.type === 'click' |
typescript
// typeof
function formatValue(value: string | number): string {
if (typeof value === 'string') return value.trim()
return value.toFixed(2)
}
// 判別ユニオン(Discriminated Union)
interface TextNotification { type: 'text'; content: string }
interface ImageNotification { type: 'image'; imageUrl: string }
type Notification = TextNotification | ImageNotification
function renderNotification(n: Notification) {
switch (n.type) {
case 'text': return n.content
case 'image': return n.imageUrl
}
}- 判別ユニオンは
switch文と組み合わせて、すべてのケースを処理します。 - 処理されていないケースをコンパイル時に検出するには、
defaultでnever型を活用します。