Using type annotations with kwargs in Python
Historically in Python, adding explicit typing for kwargs was not directly supported; however, Python 3.12 added that ability.
In this post, we’ll go over methods for adding types to the kwargs argument.
Adding type annotations to kwargs
Python 3.12 released a new method for explicitly typing kwargs: using a TypedDict + Unpack.
1from typing import NotRequired, TypedDict, Unpack
2
3
4class Parameters(TypedDict):
5 foo: int
6 bar: str
7 baz: NotRequired[str]
8
9
10def some_function(**kwargs: Unpack[Parameters]) -> None:
11 ...
12
13
14some_function(foo=1, bar="qux")Here’s what happened:
Parametersinherits fromTypedDictNotRequiredrepresents an optional argumentkwargsis typed using theUnpack[Parameters]type
mypy respects the typing.
Adding type annotations before Python 3.12
Before Python 3.12, there was not a clear way to explicitly declare typing for kwargs. The closest solutions are covered below.
Using TypedDict
Instead of using kwargs, ditch it for a TypedDict. For example:
1from typing import TypedDict
2
3
4class Parameters(TypedDict):
5 foo: int
6 bar: str
7 baz: str | None
8
9
10def some_function(params: Parameters) -> None:
11 ...
12
13
14some_function(Parameters(foo=1, bar="test", baz=None))It’s not great, but it provides structured typing and feedback using mypy.
Using Any
Simply using Any will suffice with mypy as well:
1from typing import Any
2
3
4def some_function(**kwargs: Any) -> None:
5 ...
6
7
8some_function(foo="bar")But this is obviously not type safe since it disables type checking.
Using a single type
If your use-case allows it, you may define one type for the entire kwargs argument:
1def some_function(**kwargs: str) -> None:
2 ...
3
4
5some_function(foo="bar", bar="qux")Unfortunately, this locks you into having the same type supplied per each argument.