Type Assertions & Guards
MedType assertions tell the compiler to treat a value as a specific type, overriding its inference. While assertions (`as Type`) are sometimes necessary, they bypass type checking and should be used sparingly. Type guards are the safer alternative — they perform runtime checks that TypeScript understands, narrowing types without losing safety. Knowing when to use each is critical for writing robust TypeScript.
Interactive Visualization
Key Points
- Type assertions (`as Type`) tell the compiler to trust you — no runtime check occurs
- The non-null assertion (`!`) asserts that a value is not null or undefined
- Type guards (`typeof`, `instanceof`, `in`, custom predicates) narrow types safely at runtime
- Assertion functions (`asserts val is Type`) narrow types by throwing on failure
- Const assertions (`as const`) make values immutable and preserve literal types
- Double assertions (`value as unknown as Target`) bypass assignability — a code smell
- Prefer type guards over assertions whenever possible for type safety
Code Examples
Type Assertions vs Type Guards
// UNSAFE: Type assertion — no runtime check const input = document.getElementById('name') as HTMLInputElement input.value = 'hello' // works, but crashes if element is null or not an input // SAFE: Type guard with runtime check const el = document.getElementById('name') if (el instanceof HTMLInputElement) { el.value = 'hello' // safely narrowed } // Non-null assertion — tells TS a value is not null function getFirst<T>(arr: T[]): T { return arr[0]! // asserts arr[0] is not undefined } // Safer alternative: explicit check function getFirstSafe<T>(arr: T[]): T | undefined { return arr.length > 0 ? arr[0] : undefined }
Type assertions override the compiler but provide no runtime safety. Type guards actually check at runtime, giving both type safety and runtime safety.
Const Assertions
// Without const assertion — types are widened const config = { endpoint: '/api', retries: 3, methods: ['GET', 'POST'], } // type: { endpoint: string; retries: number; methods: string[] } // With const assertion — types are narrowed to literals const configConst = { endpoint: '/api', retries: 3, methods: ['GET', 'POST'], } as const // type: { // readonly endpoint: '/api' // readonly retries: 3 // readonly methods: readonly ['GET', 'POST'] // } // Common pattern: derive a union from const array const ROLES = ['admin', 'editor', 'viewer'] as const type Role = (typeof ROLES)[number] // 'admin' | 'editor' | 'viewer' // Satisfies + as const — validate AND preserve literals const themes = { light: '#ffffff', dark: '#000000', } as const satisfies Record<string, string>
Const assertions preserve literal types and make objects deeply readonly. Combined with `typeof` and indexed access, they let you derive union types from runtime arrays.
Assertion Functions
// Assertion function — narrows type by throwing function assertDefined<T>( val: T | null | undefined, msg?: string ): asserts val is T { if (val === null || val === undefined) { throw new Error(msg ?? 'Value is null or undefined') } } // Usage function processUser(user: User | null): void { assertDefined(user, 'User must exist') // After assertion, user is narrowed to User console.log(user.name) } // Assertion function for type checking function assertIsString(val: unknown): asserts val is string { if (typeof val !== 'string') { throw new TypeError(`Expected string, got ${typeof val}`) } } interface User { name: string; age: number }
Assertion functions throw if the assertion fails, otherwise they narrow the type for the rest of the scope. They are useful for validation at function boundaries.
Common Mistakes
- Using `as` assertions to silence errors instead of fixing the underlying type issue
- Overusing the non-null assertion `!` when a proper null check is needed
- Confusing `as const` (narrows to literal types) with `as Type` (type assertion)
- Using double assertions (`as unknown as T`) which completely bypass type safety
- Writing assertion functions that do not actually throw on invalid input
Interview Tips
- Always prefer type guards over assertions — explain why in interviews
- Know the `as const` pattern for deriving union types from arrays
- Explain `satisfies` + `as const` for validation with preserved literal types
- Mention that assertions are sometimes unavoidable (DOM elements, third-party libraries)