kwargs
Two Correct Ways to Type kwargs
1. Value Type Annotation
def foo(**kwargs: int | str):
...
This is the standard way to type kwargs when you want to specify what types the values can be. The **kwargs syntax automatically handles the dictionary structure, and you only need to specify the types of values.
Example usage:
foo(a=1, b="hello") # Valid
foo(x=1.0) # Invalid - float not allowed
2. TypedDict with Unpack (Python 3.12+)
from typing import TypedDict, Unpack
class Movie(TypedDict):
name: str
year: int
def foo(**kwargs: Unpack[Movie]):
...
Use this when you need to specify exact structure of the kwargs with specific field names and their types.
Example usage:
foo(name="Life of Brian", year=1979) # Valid
foo(name="Life of Brian") # Invalid - missing year
foo(name="Life of Brian", year="1979") # Invalid - year must be int
Mark all keywords as optional with total=False
class Kw(TypedDict, total=False):
key1: int
key2: str
Mark specific keywords as optional with typing.NotRequired
class Kw(TypedDict):
key1: int
key2: NotRequired[str]
Common Mistake to Avoid
# INCORRECT:
def foo(**kwargs: dict[str, int | str]):
...
This is wrong because:
- It tries to make each value a dictionary
- The
**syntax already provides the dictionary structure - Will cause type checker errors when used
Test Results Example
def foo1(**kwargs: dict[str, int | str]): # Wrong
...
def foo2(**kwargs: int | str): # Correct
...
def foo3(**kwargs: Unpack[Movie]): # Correct
...
# Test calls
foo1(**{"name": "Life of Brian", "year": 1979}) # Error
foo2(**{"name": "Life of Brian", "year": 1979}) # OK
foo3(**{"name": "Life of Brian", "year": 1979}) # OK