typeguard review
Typeguard checks Python type annotations while code runs. You can validate one value with `check_type()`, decorate a function or class with `@typechecked`, or instrument an entire package through its import hook and pytest plugin. Those checks cover arguments, return values, annotated assignments, and generator traffic; they report mismatches instead of converting data. Version 4.6.0 adds PEP 661 sentinel support, Python 3.15 compatibility, wildcard typing-import detection, and a pytest package-ignore option. It also fixes instrumentation around assignment expressions, Protocol classes, Literal values, and empty tuple annotations, while raising the minimum Python version to 3.10.
Typeguard 4.6.0 installed in 0.3 seconds and occupied 1 MB in our sandbox, making it an inexpensive test dependency for checking annotations against real values. Install it for CI and narrow internal contracts; use a parser such as Pydantic when input must be converted or explained to an API caller.
We installed it
| Install | ✓ · 0.3s | 2 packages on disk · 1 MB |
| Import | ✓ | import typeguard in 0.48s · pure Python · py.typed · requires Python >=3.10 |
| Known vulns | 0 | (pip-audit) |
Answers from our run
Does typeguard install cleanly?
Yes. In a fresh container with an empty cache, pip install typeguard finished in 0.3s, leaving 2 packages and 1 MB on disk. pip-audit reported no known vulnerabilities.
What does typeguard need to run?
Python >=3.10, and nothing compiled: it is pure Python. In our run import typeguard succeeded in 0.48s, and the package ships py.typed for type checkers.
typeguard or beartype: which should you use?
beartype: Choose it when decorator-based runtime checking must stay cheap enough for regular production use. Typeguard 4.6.0 installed in 0.3 seconds and occupied 1 MB in our sandbox, making it an inexpensive test dependency for checking annotations against real values.
When should you not use typeguard?
The boundary needs coercion, defaults, serialization, or a structured list of user-facing errors. Typeguard rejects the value it receives; Pydantic is built for that wider job.
Use it if
- A test suite needs to catch runtime values that passed mypy or pyright because they entered through JSON, plugins, mocks, or untyped dependencies.
- You want annotation checks across an internal package during pytest runs without leaving decorators throughout application code.
- An existing TypedDict, union, Literal, or nested generic should validate one object without introducing a separate data model.
- A generator contract matters enough to check yielded values and values sent back into it, as well as ordinary arguments and returns.
- The boundary needs coercion, defaults, serialization, or a structured list of user-facing errors. Typeguard rejects the value it receives; Pydantic is built for that wider job.
- Checks would run inside a latency-sensitive production loop. Function instrumentation and collection traversal add work on every checked path.
- The target code has no inspectable Python source because it is frozen, generated at runtime, compiled with Cython, or wrapped before Typeguard sees it. The instrumenter recompiles source.
- Checking only the first collection item is unacceptable and nobody will configure the policy. FIRST_ITEM is the default; exhaustive checking requires ALL_ITEMS.
- The application still supports Python 3.9. Typeguard 4.6.0 requires Python 3.10 or newer.
- Another import hook transforms the same modules and its ordering cannot be controlled. A direct `check_type()` call or a narrow decorator is easier to reason about.
Setup reality
Our install of typeguard 4.6.0 completed in 0.3 seconds in a fresh Python 3.12 Bookworm container. It left 2 packages using 1 MB on disk, with 1 direct dependency. The distribution is pure Python, includes py.typed, and requires Python 3.10 or newer. import typeguard completed in 0.48 seconds, and pip-audit found 0 known vulnerabilities. The metadata inspected by our lab did not state a license.
Package-wide checking depends on import order. Install the hook before importing the package you want instrumented; modules already in sys.modules are left alone. The pytest plugin handles that early setup with --typeguard-packages. Version 4.6.0 also accepts --typeguard-packages-ignore, which is useful for generated modules or old code that cannot survive source rewriting. Put @typechecked closest to a function so another decorator does not hide its original body.
Typeguard examines the first item of a collection by default. That keeps a large list[int] check bounded, but a bad value at index 20 can pass unnoticed. Set CollectionCheckStrategy.ALL_ITEMS where complete traversal matters, then measure the cost with real inputs. Direct check_type() calls take their own collection and forward-reference policies; they do not silently inherit every global setting.
A mismatch raises TypeCheckError; it does not turn "7" into 7 or fill a missing field. That makes Typeguard a better fit for tests and internal contracts than request parsing. A warning callback can expose violations during a staged rollout, while CI can treat the warning class as an error. Keep production instrumentation targeted when call frequency or import-hook interaction is uncertain.
Patterns
Check a value against an annotation check-one-value
from typeguard import check_type
ports = check_type([8000, 8001], list[int])
# check_type([8000, "8001"], list[int]) raises TypeCheckErrorA passing call returns the same object, while a failing call raises `TypeCheckError`. No string-to-integer conversion occurs.
Apply a TypedDict to decoded JSON check-typed-dict
from typing import TypedDict
from typeguard import check_type
class Job(TypedDict):
id: int
queue: str
job = check_type(payload, Job)The runtime keys and values are checked against `Job`; missing fields and wrong value types are rejected rather than filled or coerced.
Check a function contract instrument-function
from typeguard import typechecked
@typechecked
def reserve(item_id: int, quantity: int) -> bool:
return inventory.reserve(item_id, quantity)Keep `@typechecked` closest to the definition. A decorator placed underneath it can prevent Typeguard from seeing the original function source.
Check every supported method on a class instrument-class
from typeguard import typechecked
@typechecked
class Cache:
def get(self, key: str) -> bytes | None:
return self._items.get(key)
def put(self, key: str, value: bytes) -> None:
self._items[key] = valueClass decoration instruments supported methods in that class. A nested class needs its own decorator.
Instrument a package before import install-import-hook
from typeguard import install_import_hook
manager = install_import_hook(
packages=["billing"],
ignore_packages=["billing.generated"],
)
import billing.jobs
# later, if needed
manager.uninstall()The hook changes only modules imported after installation. Anything already present in `sys.modules` is not revisited.
Enable checks from pytest instrument-pytest-package
pytest --typeguard-packages=billing \
+ --typeguard-packages-ignore=billing.generatedTypeguard 4.6.0 adds `--typeguard-packages-ignore`. The pytest plugin installs its hook early enough to instrument the selected package.
Traverse every collection item check-all-items
from typeguard import CollectionCheckStrategy, typechecked
@typechecked(
collection_check_strategy=CollectionCheckStrategy.ALL_ITEMS
)
def total(values: list[int]) -> int:
return sum(values)FIRST_ITEM is the default. ALL_ITEMS can find a bad later element, and its work increases with collection length.
Set the instrumentation policy once set-global-policy
from typeguard import CollectionCheckStrategy, config
config.collection_check_strategy = CollectionCheckStrategy.ALL_ITEMSGlobal configuration affects decorator and import-hook instrumentation. Pass options directly to `check_type()` when a one-off check needs a different policy.
Report mismatches as warnings warn-on-mismatch
from typeguard import config, warn_on_error
config.typecheck_fail_callback = warn_on_errorThe callback emits `TypeCheckWarning` instead of raising immediately. Configure the test runner to promote that warning when CI must fail.
Translate a failed boundary check catch-type-error
from typeguard import TypeCheckError, check_type
try:
settings = check_type(raw_settings, Settings)
except TypeCheckError as exc:
raise ConfigError(f"invalid settings: {exc}") from excCatch `TypeCheckError` specifically so parser, I/O, and application exceptions keep their original meaning.
Pause checks around trusted bulk work suppress-known-work
from typeguard import suppress_type_checks
with suppress_type_checks():
for record in trusted_snapshot:
ingest(record)Suppression covers calls made inside the context, including instrumented callees. Keep its scope narrow so unrelated checks are not hidden.
Check values yielded by a generator check-generator-contract
from collections.abc import Iterator
from typeguard import typechecked
@typechecked
def page_ids() -> Iterator[int]:
for row in fetch_pages():
yield row["id"]Each yielded value is checked when iteration reaches it. A mismatch may therefore appear after the generator was created.
Alternatives
| Package | Registry | Pick it when |
|---|---|---|
| beartype | PyPI | Choose it when decorator-based runtime checking must stay cheap enough for regular production use. |
| pydantic | PyPI | Choose it for external payloads that need parsing, coercion, defaults, serialization, and structured validation errors. |
| attrs | PyPI | Choose it when explicit validators belong on declared class fields instead of being inferred across every annotation. |
More utils guides
lru-cache · ajv · type-fest · p-limit · find-up · js-yaml · the whole shelf →
How this guide is made: grounded in the library's documentation, release notes, changelog, and issue history, on a fixed rubric — not a hands-on install of every release. The 50 most-downloaded entries are additionally install-verified in clean containers. Corrections: contact the desk.

