Type Assertions & Guards

Med

Type 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)

Related Concepts