assert_type and reveal_type: Test Type Inference

Published on: August 29, 2026
Reading time: 4 minutes
A developer typing code on a laptop with a Python book beside in an office.

typing.assert_type and typing.reveal_type help developers understand and test static inference. reveal_type() asks a checker to display the inferred type of an expression. assert_type() declares the type you expect, turning that expectation into a static test that fails when inference changes.

These tools are valuable for libraries, overloads, generics, Protocols, TypedDicts, decorators, narrowing functions, and public APIs. This guide covers exploration, automated typing tests, CI, runtime behavior, Any leakage, variadic types, Self, and checker differences.

What reveal_type does

from typing import reveal_type

value = [1, 2, 3]
reveal_type(value)

A checker may report list[int]. The diagnostic appears during static analysis. Runtime behavior is secondary and may print a message, depending on the implementation.

Revealing intermediate expressions

data: dict[str, int | None] = {"a": 1}
value = data.get("a")
reveal_type(value)

The result should be a union such as int | None. That explains why a check is required before arithmetic.

Control-flow narrowing

if value is not None:
    reveal_type(value)
    print(value + 1)

Inside the branch, the type should narrow to int. reveal_type confirms whether the checker interpreted the condition as expected.

What assert_type does

from typing import assert_type

result = len("python")
assert_type(result, int)

The checker compares the inferred type with the expected type. At runtime, the function returns the first argument and does not convert or validate it.

assert_type is not isinstance

value: object = 10
assert_type(value, int)  # expected static failure

The concrete runtime object is an integer, but the declared static type is object. assert_type tests available static information, not the observed runtime value.

Testing a generic function

from typing import TypeVar

T = TypeVar("T")

def first(items: list[T]) -> T:
    return items[0]

assert_type(first([1, 2]), int)
assert_type(first(["a", "b"]), str)

These checks confirm that the type parameter propagates to the return value.

Testing overloads

from typing import Literal, overload

@overload
def read(*, binary: Literal[False] = False) -> str: ...
@overload
def read(*, binary: Literal[True]) -> bytes: ...

def read(*, binary: bool = False) -> str | bytes:
    return b"data" if binary else "data"

assert_type(read(), str)
assert_type(read(binary=True), bytes)

Changing overload order or signatures may degrade inference. assert_type catches the regression without executing the function.

Testing Literal inference

mode = "fast"
reveal_type(mode)

mode_literal: Literal["fast"] = "fast"
assert_type(mode_literal, Literal["fast"])

A mutable variable may widen to str, while an explicit annotation preserves the literal value.

Testing TypedDict

from typing import TypedDict

class User(TypedDict):
    id: int
    name: str

user: User = {"id": 1, "name": "Ana"}
assert_type(user["id"], int)
assert_type(user["name"], str)

Optional keys should be tested before access. reveal_type is useful before and after membership checks.

Testing TypeGuard and TypeIs

from typing import TypeGuard

def is_str(value: object) -> TypeGuard[str]:
    return isinstance(value, str)

item: object = "x"
if is_str(item):
    assert_type(item, str)

The test confirms narrowing in the true branch. For TypeIs, also test the false branch. See the Python TypeGuard guide.

Testing Protocols

from typing import Protocol

class Closable(Protocol):
    def close(self) -> None: ...

class File:
    def close(self) -> None: ...

file = File()
assert_type(file, File)

closable: Closable = file
assert_type(closable, Closable)

assert_type checks the static type of the variable rather than merely structural compatibility of the original expression.

Decorators and signature loss

@my_decorator
def find(id_: int) -> str:
    ...

reveal_type(find)
assert_type(find(1), str)

Poorly typed decorators often turn precise functions into Callable[..., Any]. ParamSpec and TypeVar can preserve the contract. See Python ParamSpec.

Static test files

Create files such as tests/typing/test_api.py. They do not need to run under pytest; CI runs a checker over them. These files document the type experience expected by library users.

Positive and negative tests

assert_type expresses successful inference. Calls that should fail require checker-specific expected-error comments or typing test tools. Keep negative cases close to the public API they protect.

Do not depend on diagnostic wording

reveal_type messages vary across checkers and versions. Use assert_type for automated expectations and reveal_type for exploration.

Equivalent types

Different syntax may represent equivalent types, and union order may differ. The checker decides equivalence rather than comparing text output.

Any can hide failures

If a dependency returns Any, many operations pass unchecked. Add assert_type tests at boundaries and enable strict Any-related settings. reveal_type quickly shows where Any entered the API.

Never and unreachable branches

In exhaustive code, reveal_type may report Never. That confirms a union has been completely handled. The Python Never guide explains assert_never().

Self and fluent methods

builder = SpecialBuilder().configure()
assert_type(builder, SpecialBuilder)

This verifies that a method returning Self preserves subclasses.

Variadic types

result = add_prefix((1, "a"))
assert_type(result, tuple[str, int, str])

TypeVarTuple and Unpack can produce complex inference. Static assertions document the exact positional relationship.

Runtime behavior

assert_type(value, Type) returns value without checking it. reveal_type also does not replace runtime validation. Never use either function to protect user input.

Remove exploratory reveals

reveal_type is useful during development but may create runtime output or clutter production source. Keep diagnostic calls in dedicated typing tests or remove them after investigation.

Version compatibility

Use typing_extensions.assert_type where needed. Static behavior depends heavily on the checker version. Pin checker versions in CI and review upgrades deliberately.

Comparing checkers

Mypy and pyright may infer complex expressions differently. If a library supports both, run the same typing suite with both and avoid relying on unspecified behavior.

Common mistakes

  • Expecting runtime validation: assert_type does not call isinstance.
  • Leaving reveal_type in production paths: it may produce unnecessary output.
  • Testing only implementation internals: test the consumer-facing API.
  • Allowing Any to spread unnoticed: assertions lose value.
  • Comparing diagnostic strings: wording changes.
  • Not pinning the checker: upgrades may alter inference.

Complete example: cache API

from typing import TypeVar, overload, assert_type

T = TypeVar("T")
_MISSING = object()

@overload
def get(key: str) -> object: ...
@overload
def get(key: str, default: T) -> object | T: ...

def get(key: str, default: object = _MISSING) -> object:
    ...

assert_type(get("x"), object)
assert_type(get("x", 0), object | int)
assert_type(get("x", None), object | None)

The tests document the relationship between defaults and return types. CI reports a regression when overloads or stubs change.

Library testing strategy

Create realistic consumer files. Import the installed library rather than internal modules. Test return inference, expected failures, subclasses, overloads, generics, and stub packaging. Public typing can break even when runtime tests remain green.

Conclusion

reveal_type() is a lens into checker inference. assert_type() turns an expected type into a regression test. Together they make typed APIs more predictable and maintainable.

The official Python assert_type and reveal_type documentation defines their behavior. Use reveal_type to explore, assert_type to automate, and run the tests with the exact checker configurations supported by the project.

Share:

Facebook
WhatsApp
Twitter
LinkedIn

Article content

    Related articles

    High-angle view of woman coding on a laptop, with a Python book nearby. Ideal for programming and tech content.
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    Python get_type_hints: Resolve Annotations

    Learn Python get_type_hints to resolve forward references, preserve Annotated metadata, and inspect functions and classes safely.

    Ler mais

    Tempo de leitura: 5 minutos
    29/08/2026
    A person typing on a laptop with a Python programming book visible, capturing technology and learning.
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    Python runtime_checkable: Protocol at Runtime

    Learn Python runtime_checkable to test Protocols with isinstance, understand its limits, and design safer structural contracts.

    Ler mais

    Tempo de leitura: 5 minutos
    29/08/2026
    A person typing on a laptop with a Python programming book visible, capturing technology and learning.
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    Python Unpack: Typed kwargs and Variadics

    Learn Python Unpack to type **kwargs with TypedDict, expand variadic tuples, and preserve precise callable signatures.

    Ler mais

    Tempo de leitura: 4 minutos
    29/08/2026
    A person typing on a laptop with a Python programming book visible, capturing technology and learning.
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    Required and NotRequired: Optional TypedDict Keys

    Learn Python Required and NotRequired to control mandatory and optional TypedDict keys without confusing absence with None.

    Ler mais

    Tempo de leitura: 5 minutos
    29/08/2026
    High-angle view of woman coding on a laptop, with a Python book nearby. Ideal for programming and tech content.
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    Python ReadOnly: Protect TypedDict Fields

    Learn Python ReadOnly to protect TypedDict fields, model stable response contracts, and prevent accidental writes during static analysis.

    Ler mais

    Tempo de leitura: 5 minutos
    29/08/2026
    Close-up of hands typing on a laptop keyboard, Python book in sight, coding in progress.
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    Python TypeAliasType: Runtime Type Aliases

    Learn Python TypeAliasType to create explicit aliases, inspect them at runtime, and model reusable generic APIs safely.

    Ler mais

    Tempo de leitura: 6 minutos
    29/08/2026