← CodeClarityLab Home
Browse by Category
+ added · updated 7d
← Back to glossary

Type Guards & Narrowing

typescript 2.0 Intermediate

Also Known As

type narrowing user-defined type guards is predicate type predicates

TL;DR

Type guards are expressions that narrow a union type to a more specific type at runtime — telling TypeScript exactly which branch of a union you're in.

Explanation

TypeScript narrows types inside conditional branches automatically (typeof, instanceof, truthiness). Custom type guards use the is predicate: function isString(v: unknown): v is string { return typeof v === 'string'; }. Discriminated union narrowing works via a shared literal field (kind, type). The in operator narrows to types that have a given property. Exhaustiveness checks with never ensure all cases are handled. Common pattern: unknown input → series of type guards → fully typed value. Assertion functions (asserts v is T) throw instead of returning false.

Diagram

flowchart TD
    INPUT["input: string | number | unknown"]
    CHECK{Type guard check}
    INPUT --> CHECK
    CHECK -- typeof === string --> STR[Narrowed: string<br/>toUpperCase safe]
    CHECK -- typeof === number --> NUM[Narrowed: number<br/>toFixed safe]
    CHECK -- is predicate --> CUSTOM[Custom narrowing<br/>v is MyType]
    CHECK -- discriminant field --> DISC[kind === circle<br/>radius available]
    style STR fill:#238636,color:#fff
    style NUM fill:#238636,color:#fff

Watch Out

A type guard with is predicate only narrows in the calling scope — if the function body itself has a bug (returns true unconditionally), TypeScript trusts it and the narrowing will be wrong at runtime.

Common Misconception

Type guards only work with typeof and instanceof — user-defined is predicates and discriminated unions are far more powerful and the standard approach for domain types.

Why It Matters

Without type guards, working with union types and unknown inputs forces unsafe casts (as Type) that silently break at runtime — guards make narrowing explicit and compiler-verified.

Common Mistakes

  • Using as Type (type assertion) instead of a proper type guard — assertions bypass the compiler and lie about the type.
  • Forgetting the is predicate return type — without it, TypeScript doesn't narrow in the calling scope.
  • Checking instanceof on plain objects — use in or a discriminant field instead.
  • Not handling the never case in exhaustive switches — add a default branch that assigns to never to catch missing cases.

Avoid When

  • Avoid writing type guards for every minor check — inline typeof/instanceof is fine for simple cases.

When To Use

  • Processing unknown or any input from external sources (API responses, JSON.parse, user input).
  • Working with union types that need different handling per variant.
  • Building exhaustive switch statements over discriminated unions.

Code Examples

💡 Note
Shows unsafe casting replaced by a proper is predicate guard, plus discriminated union narrowing with an exhaustive never check.
✗ Vulnerable
function processInput(input: string | number) {
    // Unsafe cast — no runtime check
    const s = input as string;
    console.log(s.toUpperCase()); // Crashes if input is number
}
✓ Fixed
// User-defined type guard
function isString(v: unknown): v is string {
    return typeof v === 'string';
}

function processInput(input: string | number) {
    if (isString(input)) {
        console.log(input.toUpperCase()); // input is string here
    } else {
        console.log(input.toFixed(2));    // input is number here
    }
}

// Discriminated union narrowing
type Shape = { kind: 'circle'; radius: number } | { kind: 'rect'; width: number; height: number };
function area(s: Shape): number {
    switch (s.kind) {
        case 'circle': return Math.PI * s.radius ** 2;
        case 'rect':   return s.width * s.height;
        default: const _exhaustive: never = s; return _exhaustive;
    }
}

Added 11 Apr 2026
Edited 19 Apr 2026
Views 19
Rate this term
No ratings yet
🤖 AI Guestbook educational data only
| |
Last 30 days
0 pings W 0 pings T 0 pings F 0 pings S 0 pings S 0 pings M 0 pings T 1 ping W 0 pings T 1 ping F 0 pings S 1 ping S 1 ping M 0 pings T 0 pings W 0 pings T 1 ping F 1 ping S 0 pings S 0 pings M 0 pings T 0 pings W 2 pings T 0 pings F 0 pings S 1 ping S 0 pings M 0 pings T 0 pings W 0 pings T
No pings yet today
No pings yesterday
Google 3 Perplexity 3 SEMrush 3 ChatGPT 1 Unknown AI 1 Ahrefs 1 Meta AI 1
crawler 12 crawler_json 1
DEV INTEL Tools & Severity
🟠 High ⚙ Fix effort: Low
⚡ Quick Fix
Replace 'as Type' casts with 'v is Type' predicate functions. Use discriminant literal fields on union members for switch-based narrowing.
📦 Applies To
typescript 2.0 web cli
🔗 Prerequisites
🔍 Detection Hints
as [A-Z][a-zA-Z]+(?!s\b)
Auto-detectable: ✓ Yes typescript eslint
⚠ Related Problems
🤖 AI Agent
Confidence: Medium False Positives: Medium ✗ Manual fix Fix: Medium Context: Function

✓ schema.org compliant