Python type hints – How to narrow down the number of types with TypeGuard


I have previously talked about type narrowing with isinstance(), assert and Literal. In today’s post, we’ll look at TypeGuarda new special type that allows us to create custom type narrowing functions.

TypeGuard was defined in PEP 647 and is available in Python 3.10+ or ​​older versions from typing-extensions. Guido van Rossum added support to Mypy in version 0.900, which was published the day before.

Recall that type narrowing uses certain expressions to infer that, in a given block, a variable has a more restricted type than its definition. For example, using isinstance():

from __future__ import annotations

name: str | None

if isinstance(name, str):
    # name must be 'str'
    ...
else:
    # name must be None
    ...

Typecheckers, including Mypy, support a limited number of expressions such as if isinstance(...). But the number of potentially type-restricting expressions is endless, especially for parameterized types like containers. TypeGuard allows us to write any type expression and tell our type checker that it narrows them down.

A type narrowing function is one that takes at least one argument and returns bool. Instead of denoting the return type as boolwe use TypeGuard[T]where True means the first argument is of type Ta False – No. Let’s take this example, adapted from PEP:

from __future__ import annotations

from typing_extensions import TypeGuard


def is_str_list(value: list[object]) -> TypeGuard[list[str]]:
    """Are all list items strings?"""
    return all(isinstance(x, str) for x in value)


x: list[object]

reveal_type(x)
if is_str_list(x):
    reveal_type(x)

is_str_list() returns Trueif the given list contains only strings. We tell Mypy that it can narrow down the type value before list[str] using return type TypeGuard.

Running Mypy on this file, we see the following result call reveal_type():

$ mypy --strict example.py
example.py:13: note: Revealed type is "builtins.list[builtins.object]"
example.py:15: note: Revealed type is "builtins.list[builtins.str]"

The second note shows that Mypy knows that x must be a list of strings in a block if. This allows us to use the elements of the list as str without any errors. Fine!

TypeGuard is flexible because it allows us to write arbitrary code to narrow expressions. True, it forces us to wrap even short expressions in separate functions, but this is often useful for code readability.

Since there are an infinite number of possible expressions, type checkers cannot confirm that the expressions we choose match the protected types. Therefore, the functions TypeGuard should be written with care and tested thoroughly.

PEP 647 also shows common features TypeGuard with TypeVar, but when I tried the examples, I found that Mypy 0.901 doesn’t support them yet. For TypeGuard has several open issuesso it looks like Mypy can benefit from our input on their solution!

And keep your types well protected.


We invite everyone to an open lesson on the topic “Introduction to web development with Flask“. In this lesson, we will introduce you to the basics of Flask web development, as well as learn how to create and render page templates. Let’s try to create a Flask application, then create routes, and finally process various HTTP methods in Flask. Registration link.

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *