Skip to content

タイプガードおよびアサーション

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?.name

11.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 文と組み合わせて、すべてのケースを処理します。
  • 処理されていないケースをコンパイル時に検出するには、defaultnever 型を活用します。

TIENIPIA QUALIFIED STANDARD