Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.syntblaze.com/llms.txt

Use this file to discover all available pages before exploring further.

typing.TypeGuard is a special typing construct introduced in PEP 647 (Python 3.10) that enables user-defined type narrowing. It instructs static type checkers (such as mypy or pyright) that if a boolean-returning function evaluates to True, a specific argument passed to that function is guaranteed to be of the type specified within the TypeGuard. At runtime, TypeGuard[T] behaves exactly like a standard bool return type annotation. Its sole purpose is to influence static flow analysis in the calling scope.

Syntax

A function utilizing a type guard must return a boolean value and annotate its return type as TypeGuard[TargetType].
from typing import TypeGuard, Any

def is_string(val: Any) -> TypeGuard[str]:
    # Runtime logic that validates the type of 'val'
    return isinstance(val, str) 

Type Checker Mechanics

When a static type checker encounters a function returning TypeGuard[T], it applies specific narrowing rules to the calling scope based on the control flow:
  1. The True Branch (Positive Narrowing): If the type guard function returns True, the type checker narrows the type of the passed argument to T within the corresponding if block.
  2. The False Branch (No Negative Narrowing): If the type guard function returns False, the type checker does not perform negative narrowing. The argument retains its original, un-narrowed type in the else block. The type checker does not subtract the guarded type from the original type because a TypeGuard function can contain arbitrary runtime logic and value checks (e.g., val.isalnum() or len(val) > 0). A False return only implies that the specific validation failed, which does not necessarily mean the argument is strictly not of type T.
from typing import TypeGuard

def is_string_list(val: list[object]) -> TypeGuard[list[str]]:
    return all(isinstance(x, str) for x in val)

def process(data: list[object]) -> None:
    if is_string_list(data):
        # Type checker evaluates 'data' as list[str]
        print(data) 
    else:
        # Type checker evaluates 'data' as list[object]
        # It DOES NOT narrow to a type excluding list[str]
        print(data) 

Technical Constraints

  • Argument Binding: A TypeGuard strictly and exclusively narrows the argument bound to the first parameter defined in the function signature, regardless of whether the caller passes it as a positional or a keyword argument. When applied to an instance method or a class method, TypeGuard narrows the argument bound to the second parameter (the first explicit parameter immediately following self or cls). It cannot be configured to target any other parameter.
  • Implicit Trust: Static type checkers do not verify the internal logic of the function returning the TypeGuard. The type checker implicitly trusts that the developer’s runtime implementation accurately guarantees the type T. If the internal logic is flawed, it will introduce unsoundness into the type system.
  • Type Overriding: According to PEP 647, TypeGuard does not require the target type T to be a strict subtype of the original argument’s type. It allows narrowing to an entirely unrelated type hierarchy. In such cases, TypeGuard effectively acts as an unsafe cast in the positive branch, completely overriding the original type rather than intersecting it.
(Note: Because TypeGuard allows unsafe casting to unrelated types and lacks negative narrowing in the False branch, Python 3.13 introduced typing.TypeIs via PEP 742. TypeIs enforces stricter type intersection, ensuring the narrowed type is a valid subtype, and allows for both positive and negative type narrowing.)
Master Python with Deep Grasping Methodology!Learn More