To extend the types supported by Typeguard, you must implement two components: a type checker lookup function and one or more type checker functions.
1. The Type Checker Lookup Function
This function determines if a custom type checker should be used for a given annotation. It receives three arguments:
origin_type: The base type (e.g., tuple from tuple[int]).args: The generic arguments stripped from the annotation (e.g., (int,) from tuple[int]).extras: Extra arguments from typing.Annotated (e.g., ('foo',) from Annotated[int, 'foo']).
It must return a TypeCheckerCallable or None if no match is found.
2. The Type Checker Function
This function performs the actual validation. It receives four arguments:
value: The actual value being checked.origin_type: The origin type.args: The generic arguments (an empty tuple if not parametrized).memo: A TypeCheckMemo object.
Important Implementation Rules:
- Recursive Checks: If your checker needs to validate nested elements (like items in a collection), use
check_type_internal and pass the memo object along. - Configuration Compliance: Since Typeguard 4.0, checker functions must respect settings in
memo.config (specifically memo.config.collection_check_strategy) rather than relying on global configuration.
from __future__ import annotations
from inspect import isclass
from typing import Any
from typeguard import TypeCheckError, TypeCheckerCallable, TypeCheckMemo
class MySpecialType:
pass
def check_my_special_type(
value: Any, origin_type: Any, args: tuple[Any, ...], memo: TypeCheckMemo
) -> None:
if not isinstance(value, MySpecialType):
raise TypeCheckError('is not my special type')
def my_checker_lookup(
origin_type: Any, args: tuple[Any, ...], extras: tuple[Any, ...]
) -> TypeCheckerCallable | None:
if isclass(origin_type) and issubclass(origin_type, MySpecialType):
return check_my_special_type
return None