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.TypeIs is a special typing form introduced in Python 3.13 (via PEP 742) used to annotate the return type of user-defined type guard functions. It instructs static type checkers to narrow the type of a function’s argument in both the positive (True) and negative (False) control flow branches based on the boolean return value.

Syntax

TypeIs is parameterized with a single type and is exclusively used as a return type annotation.
from typing import TypeIs, Any

def is_string(variable: Any) -> TypeIs[str]:
    return isinstance(variable, str)

Type Narrowing Mechanics

When a function annotated with -> TypeIs[T] is evaluated in a conditional statement, the static type checker modifies the type of the passed argument within the resulting control flow graph:
  1. Positive Branch (True): The type checker calculates the intersection of the argument’s original type and T. The argument is narrowed to T.
  2. Negative Branch (False): The type checker calculates the type complement by subtracting T from the argument’s original type. The argument is narrowed to the remaining types.
from typing import TypeIs, reveal_type

def is_string(val: object) -> TypeIs[str]:
    return isinstance(val, str)

def process(data: str | int | list):
    if is_string(data):
        # Positive branch: Type is narrowed to `str`
        reveal_type(data)  # Revealed type is 'str'
    else:
        # Negative branch: `str` is subtracted from the union
        reveal_type(data)  # Revealed type is 'int | list'

TypeIs vs. TypeGuard

TypeIs was introduced to resolve a specific theoretical limitation of its predecessor, typing.TypeGuard (PEP 647).
  • TypeGuard[T] (Non-strict narrowing): Only narrows the type in the positive branch. If the function returns False, the type checker leaves the original type unmodified. This is necessary for functions that check for a specific property rather than an exhaustive type (e.g., is_non_empty_list).
  • TypeIs[T] (Strict narrowing): Narrows the type in both branches. It guarantees to the type checker that the boolean return value perfectly partitions the type space.
from typing import TypeGuard, TypeIs, reveal_type

def guard_str(val: str | int) -> TypeGuard[str]:
    return isinstance(val, str)

def is_str(val: str | int) -> TypeIs[str]:
    return isinstance(val, str)

def evaluate_guard(x: str | int):
    if guard_str(x):
        reveal_type(x) # str
    else:
        reveal_type(x) # str | int (Unchanged)

def evaluate_is(x: str | int):
    if is_str(x):
        reveal_type(x) # str
    else:
        reveal_type(x) # int (Strictly narrowed)

Structural Constraints

To be valid under static type checking rules, a TypeIs function must adhere to the following constraints:
  • Runtime Return Type: The function must return a boolean value at runtime.
  • Argument Binding: The narrowing effect applies implicitly to the first positional argument of the function. If TypeIs is used as the return type of an instance method or class method, the type checker automatically skips the implicit self or cls parameter and applies the narrowing to the first positional argument after self or cls.
  • Type Intersection and Disjointness: The target type T in TypeIs[T] should intersect with the first argument’s annotated type. If T is entirely disjoint from the argument’s type (e.g., checking if an int is a str), the type checker evaluates the positive branch as Never (unreachable), because the intersection of disjoint types is empty.
Master Python with Deep Grasping Methodology!Learn More