Skip to content

Type Guards and Assertions ​

11.4.1. User-Defined Type Guards ​

Use the is keyword to write type guard functions.

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) // Narrowed to AdminUser
  }
}
  • Type guard function names must start with the is prefix.
  • When complex conditional branching is repeated, extract it into a type guard for reuse.

11.4.2. as Assertion Usage Criteria ​

The as type assertion bypasses the compiler's type checking and must be used sparingly.

Permitted cases:

typescript
const canvas = document.getElementById('chart') as HTMLCanvasElement
const target = (event as MouseEvent).target as HTMLInputElement

Prohibited cases:

typescript
const data = fetchData() as UserProfile           // Unfounded assertion
const value = rawData as any as SpecificType       // Double assertion

The non-null assertion operator (!) must not be used. Replace it with optional chaining (?.) or explicit null checks.

typescript
// Incorrect
const name = user!.name
// Correct
const name = user?.name

11.4.3. unknown vs any ​

Data originating from external sources must be received as the unknown type and narrowed using type guards before use.

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 disables type checking, while unknown enforces type verification before use.
  • Error objects in catch blocks are of type unknown, so instanceof checks must be performed.

11.4.4. Type Narrowing Patterns ​

Use TypeScript's type narrowing mechanisms as appropriate for the situation.

PatternPurposeExample
typeofPrimitive type discriminationtypeof x === 'string'
instanceofClass instance discriminationerror instanceof TypeError
inProperty existence check'email' in user
Discriminated unionBranching by common discriminant propertyevent.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
  }
}
  • Discriminated unions should be used with switch statements to handle all cases.
  • To detect unhandled cases at compile time, use the never type in the default branch.

TIENIPIA QUALIFIED STANDARD