foo.py
:
kwargs = {"a": 1, "b": "c"}
def consume(*, a: int, b: str) -> None:
pass
consume(**kwargs)
mypy foo.py
:
error: Argument 1 to "consume" has incompatible type "**Dict[str, object]"; expected "int"
error: Argument 1 to "consume" has incompatible type "**Dict[str, object]"; expected "str"
This is because object
is a supertype of int
and str
, and is therefore inferred. If I declare:
from typing import TypedDict
class KWArgs(TypedDict):
a: int
b: str
and then annotate kwargs
as KWArgs
, the mypy
check passes. This achieves type safety, but requires me to duplicate the keyword argument names and types for consume
in KWArgs
. Is there a way to generate this TypedDict
from the function signature at type checking time, such that I can minimize the duplication in maintenance?
This will be available in Python 3.12 via PEP 692:
from typing import TypedDict, Unpack, Required, NotRequired
class KWArgs(TypedDict):
a: Required[int]
b: NotRequired[str]
def consume(**kwargs: Unpack[KWArgs]) -> None:
a = kwargs["a"]
b = kwargs.get("b", ...)
consume() # Not allowed.
consume(a=1) # Allowed.
consume(a=1, b="abc") # Allowed.