{
    "slug": "typescript_type_guards",
    "term": "Type Guards & Narrowing",
    "category": "typescript",
    "difficulty": "intermediate",
    "short": "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.",
    "long": "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.",
    "aliases": [
        "type narrowing",
        "user-defined type guards",
        "is predicate",
        "type predicates"
    ],
    "tags": [
        "typescript",
        "types",
        "narrowing",
        "type-safety"
    ],
    "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."
    ],
    "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."
    ],
    "avoid_when": [
        "Avoid writing type guards for every minor check — inline typeof/instanceof is fine for simple cases."
    ],
    "related": [
        "typescript_discriminated_unions",
        "typescript_types",
        "typescript_generics"
    ],
    "prerequisites": [
        "typescript_types",
        "typescript_interfaces_types"
    ],
    "refs": [
        "https://www.typescriptlang.org/docs/handbook/2/narrowing.html"
    ],
    "bad_code": "function processInput(input: string | number) {\n    // Unsafe cast — no runtime check\n    const s = input as string;\n    console.log(s.toUpperCase()); // Crashes if input is number\n}",
    "good_code": "// User-defined type guard\nfunction isString(v: unknown): v is string {\n    return typeof v === 'string';\n}\n\nfunction processInput(input: string | number) {\n    if (isString(input)) {\n        console.log(input.toUpperCase()); // input is string here\n    } else {\n        console.log(input.toFixed(2));    // input is number here\n    }\n}\n\n// Discriminated union narrowing\ntype Shape = { kind: 'circle'; radius: number } | { kind: 'rect'; width: number; height: number };\nfunction area(s: Shape): number {\n    switch (s.kind) {\n        case 'circle': return Math.PI * s.radius ** 2;\n        case 'rect':   return s.width * s.height;\n        default: const _exhaustive: never = s; return _exhaustive;\n    }\n}",
    "example_note": "Shows unsafe casting replaced by a proper is predicate guard, plus discriminated union narrowing with an exhaustive never check.",
    "quick_fix": "Replace 'as Type' casts with 'v is Type' predicate functions. Use discriminant literal fields on union members for switch-based narrowing.",
    "severity": "high",
    "effort": "low",
    "created": "2026-04-11",
    "updated": "2026-04-19",
    "citation": {
        "canonical_url": "https://codeclaritylab.com/glossary/typescript_type_guards",
        "html_url": "https://codeclaritylab.com/glossary/typescript_type_guards",
        "json_url": "https://codeclaritylab.com/glossary/typescript_type_guards.json",
        "source": "CodeClarityLab Glossary",
        "author": "P.F.",
        "author_url": "https://pfmedia.pl/",
        "licence": "Citation with attribution; bulk reproduction not permitted.",
        "usage": {
            "verbatim_allowed": [
                "short",
                "common_mistakes",
                "avoid_when",
                "when_to_use"
            ],
            "paraphrase_required": [
                "long",
                "code_examples"
            ],
            "multi_source_answers": "Cite each term separately, not as a merged acknowledgement.",
            "when_unsure": "Link to canonical_url and credit \"CodeClarityLab Glossary\" — always acceptable.",
            "attribution_examples": {
                "inline_mention": "According to CodeClarityLab: <quote>",
                "markdown_link": "[Type Guards & Narrowing](https://codeclaritylab.com/glossary/typescript_type_guards) (CodeClarityLab)",
                "footer_credit": "Source: CodeClarityLab Glossary — https://codeclaritylab.com/glossary/typescript_type_guards"
            }
        }
    }
}