Concept lesson

Strict Null Checks & Type Narrowing

Strict null checks, nullish coalescing, optional chaining, type predicates, and assertions.

lesson
Freshness: current14 min read
Mastery
not started · 0%

Learning outcomes

  • Enforce strict null safety across APIs
  • Write custom type predicates and assertion functions

Mental model

In strict mode (strictNullChecks: true), null and undefined are distinct domain types, eliminating Tony Hoare's "billion-dollar mistake" of unhandled null pointer dereferences.

Nullable Union T
null
Control Flow Branching (if / optional chain)
Custom Type Predicate val is T
Guaranteed Non-Null Type Narrowing
Conceptual teaching model synthesized from:TypeScript 5.4 Language Specification & Type System Mechanics

Theory

TypeScript's control flow engine tracks variable assignments, truthiness checks, equality guards, and custom type predicates (value is User). Custom assertion functions (asserts condition) narrow types in the calling scope by throwing an error if the condition evaluates to false.

export interface UserProfile {
  id: string;
  email: string;
  avatarUrl?: string | null;
}

export function isDefined<T>(val: T | null | undefined): val is T {
  return val !== null && val !== undefined;
}

export function assertNonNull<T>(val: T | null | undefined, msg: string): asserts val is T {
  if (val === null || val === undefined) {
    throw new Error(`[Assertion Error]: ${msg}`);
  }
}

export function processUserAvatar(user: UserProfile): string {
  const avatar = user.avatarUrl?.trim() ?? "https://cdn.example.com/default-avatar.png";
  assertNonNull(user.email, "User must have a valid email address");
  return `${user.email}:${avatar}`;
}

Alternatives and trade-offs

  • Non-null assertion operator (!): Tells TypeScript value! is non-null without checking. Risky; throws runtime TypeError if wrong.
  • Explicit Type Guards (isDefined): Type-safe runtime check that narrows types automatically.

Failure modes and misconceptions

  1. || vs ?? Operator: The logical OR operator (||) falls back on falsy values ("", 0, false). Nullish coalescing (??) falls back ONLY on null or undefined.
  2. Abusing ! assertion: Using user!.email bypasses type checking without runtime protection.
Reflect before revealing the guide

Decision scenario

Use nullish coalescing (??) when providing fallbacks for missing API string or numeric fields. Avoid the non-null assertion operator (!) in production application code.

Learning outcomes

  • Configure strictNullChecks to eliminate unhandled null pointer bugs.
  • Implement custom type predicates (val is T) for collection filtering.
  • Author scope-narrowing assertion functions (asserts val is T).

Trade-offs

Strict null checking prevents runtime null reference exceptions, but requires explicit guards or default fallbacks whenever handling optional network data.

Evidence assessment

Theory and decision mastery

not-started · 0%
theory0%
decision0%
activityNot mapped
projectNot mapped
1. What is the key difference between logical OR (||) and nullish coalescing (??) in JavaScript/TypeScript?
2. What signature defines a custom TypeScript assertion function that narrows a variable in the calling scope?
3. Why is overusing the non-null assertion operator (user!.name) considered risky?

Decision scenario

A team is building an API client where network responses contain optional fields (avatarUrl).

Which pattern safely provides a default avatar URL when avatarUrl is missing or null?

Primary sources