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
isprefix. - 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 HTMLInputElementProhibited cases:
typescript
const data = fetchData() as UserProfile // Unfounded assertion
const value = rawData as any as SpecificType // Double assertionThe 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?.name11.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)
}
}anydisables type checking, whileunknownenforces type verification before use.- Error objects in
catchblocks are of typeunknown, soinstanceofchecks must be performed.
11.4.4. Type Narrowing Patterns
Use TypeScript's type narrowing mechanisms as appropriate for the situation.
| Pattern | Purpose | Example |
|---|---|---|
typeof | Primitive type discrimination | typeof x === 'string' |
instanceof | Class instance discrimination | error instanceof TypeError |
in | Property existence check | 'email' in user |
| Discriminated union | Branching by common discriminant property | 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
}
}- Discriminated unions should be used with
switchstatements to handle all cases. - To detect unhandled cases at compile time, use the
nevertype in thedefaultbranch.