Interview frequency: …
Short answer
They declare that a boolean predicate function, when it returns True, has narrowed the type of its argument; TypeIs (since Python 3.13) narrows the type in both the True and False branches, while TypeGuard narrows only the True branch.
Explanation
When you write a custom check function such as is_str_list, the checker cannot infer that the list has become list[str] after the check; this is where TypeGuard or TypeIs comes in. TypeIs is usually the better choice because its behavior resembles isinstance. But the checker fully trusts your declaration: if the check function is wrong, the annotation will hide a bug instead of preventing it.
Code example
from typing import TypeIs
def is_str_list(values: list[object]) -> TypeIs[list[str]]:
return all(isinstance(v, str) for v in values)
def handle(values: list[object]) -> None:
if is_str_list(values):
print(", ".join(values)) # Type list[str]Common mistakes
- Writing a predicate that does not check every element and then declaring a TypeGuard for the whole list.
- Using cast everywhere instead of one trusted check function.