I know I can type a class decorator as follows:
T = TypeVar('T', bound=Type[Any])
def decorator(cls: T) -> T:
...
Which can be used:
@decorator
class Foo:
foo: int
And a reference to foo
on instance of Foo
will check properly in mypy.
But how do I type a function that returns a decorator, so that the decorated class types correctly? E.g. how can I fix the code below, if the arguments to decorator
have types not associated with the type of the wrapped class?
T = TypeVar('T', bound=Type[Any])
Decorator: TypeAlias = Callable[[T], T]
def decorator(...) -> Decorator[Any]:
def wrapper(cls: T) -> T:
# some code modifying cls depending on args to "decorator"
...
return cls
return wrapper
# now instance don't type write
@decorator(...)
class Foo:
foo: int
Once the wrapper types become complex enough, I find that using typing.Protocol
can be pretty useful. The function returned can be represented as non-generic class which supplies a generic __call__
method.
import typing as t
T = t.TypeVar('T', bound=t.Type[t.Any])
class Decorator(t.Protocol):
def __call__(self, cls: T) -> T:
...
def decorator() -> Decorator:
def wrapper(cls: T) -> T:
# some code modifying cls depending on args to "decorator"
...
return cls
return wrapper
@decorator()
class Foo:
foo: int
t.reveal_type(Foo)
t.assert_type(Foo(), Foo)
(venv) > mypy --strict main.py
main.py:25: note: Revealed type is "def () -> main.Foo"
Success: no issues found in 1 source file