snippetpythonModeratepending
Python Type Narrowing with TypeGuard and assert
Viewed 0 times
TypeGuardTypeIstype narrowingisinstancemypytyping
Problem
Type checkers don't understand custom validation logic. After checking isinstance or validating data, the type system still sees the broader type.
Solution
Type narrowing patterns for Python:
from typing import TypeGuard, Union, assert_type
# isinstance narrowing (built-in)
def process(val: Union[str, int]) -> str:
if isinstance(val, str):
return val.upper() # mypy knows val is str here
return str(val) # mypy knows val is int here
# TypeGuard for custom checks (Python 3.10+)
def is_string_list(val: list[object]) -> TypeGuard[list[str]]:
return all(isinstance(x, str) for x in val)
def greet_all(items: list[object]) -> None:
if is_string_list(items):
for item in items: # mypy knows item is str
print(item.upper())
# assert for narrowing
def get_user(user_id: int) -> User | None:
return db.get(user_id)
def update_user(user_id: int) -> None:
user = get_user(user_id)
assert user is not None, f'User {user_id} not found'
user.name = 'Updated' # mypy knows user is User, not None
# Literal narrowing
from typing import Literal
def handle_status(status: Literal['ok', 'error', 'pending']) -> None:
if status == 'ok':
pass # status is Literal['ok'] here
# TypeIs (Python 3.13+) - narrows in both branches
from typing import TypeIs
def is_str(val: object) -> TypeIs[str]:
return isinstance(val, str)Why
Type narrowing lets the type checker understand runtime checks, catching bugs at development time instead of production. Without proper narrowing, you end up with excessive type: ignore comments.
Gotchas
- TypeGuard only narrows in the True branch; TypeIs (3.13+) narrows both branches
- assert statements are stripped with python -O - don't use for validation of untrusted input
Context
Writing type-safe Python code
Revisions (0)
No revisions yet.