Skip to content

Type Narrowing

The quality of real-world typing often depends more on narrowing than on declarations. If you cannot safely narrow broad input types after runtime checks, codebases tend to drift toward casts and `Any`.

Quick takeaway: narrowing is how runtime checks become static type information. `isinstance`, `match`, `TypeGuard`, and `TypeIs` let you replace many unsafe casts with explicit, checkable flow.

Narrowing Picture

A broad input type becomes more precise after a runtime check, producing narrower branches for the type checker.

TypeGuard vs TypeIs

py
from typing import TypeGuard, TypeIs


def is_str_list(values: list[object]) -> TypeGuard[list[str]]:
    return all(isinstance(value, str) for value in values)


def is_int(value: int | str) -> TypeIs[int]:
    return isinstance(value, int)

`TypeGuard` is good when a complex structure should be treated as a more specific type. `TypeIs` is better for true type predicates that behave more like `isinstance` checks.

match Helps Too

py
def describe(value: int | str | None) -> str:
    match value:
        case int():
            return "int"
        case str():
            return "str"
        case None:
            return "none"

Truthiness Narrowing Has Limits

  • if value: collapses empty strings, zero, empty collections, and None.
  • If your design must distinguish "missing" from "empty," truthiness alone is too blunt.

Practical Connections

  • input validation helpers
  • discriminator-based branching
  • API payload parsing

Checklist

Prefer narrowing over casting

If possible, make runtime checks visible to the type checker instead of scattering `cast()` calls.

Do not overuse truthiness

Explicit comparisons are safer when empty and missing values mean different things.

Choose `TypeGuard` vs `TypeIs` carefully

`TypeGuard` fits structural refinement; `TypeIs` fits true type predicates.

Use discriminators

Tagged unions with explicit `kind` or `type` fields make both runtime logic and narrowing much cleaner.

Official Sources

Built with VitePress for a Python 3.14 handbook.