2024-07-17

Using TypeIs

PEP 742 adds TypeIs, it is very similar to TypeGuard (docs), but makes up for a particular shortcoming.

Support

TypeIs will be present in Python 3.13, but you can use it (at time of writing) currently - it is exists in typing_extensions>=4.10.0 and is supported by mypy>=1.10.1.

Example

First, some preamble:

from typing import Any, TypeVar, TypeGuard
from typing_extensions import TypeIs
T = TypeVar("T")

If we use a plain ol' isinstance, types are correctly narrowed in both branches of this if statement:

x: int | str
if isinstance_b(x, int):
    reveal_type(x)  # int
else:
    reveal_type(x)  # str

If we implement out own isinstance using TypeGuard to help the typechecker, this doesn't work properly (note that it does work in the positive case):

def isinstance_a(v: Any, cls: type[T]) -> TypeGuard[T]:
    return isinstance(v, cls)

x: int | str
if isinstance_a(x, int):
    reveal_type(x)  # int
else:
    reveal_type(x)  # int | str

TypeIs makes up for this, we can now correctly implement our own isinstance for both branches, yay!

def isinstance_b(v: Any, cls: type[T]) -> TypeIs[T]:
    return isinstance(v, cls)

x: int | str
if isinstance_b(x, int):
    reveal_type(x)  # int
else:
    reveal_type(x)  # str