I agree with Franek. A TypedDict is effectively a protocol (a structural type) that describes a typed dictionary. It is theoretically possible to manually define a protocol class that is the equivalent of a TypedDict, but it would involve so much verbose boilerplate that no one would ever do it. Consider the following: ```python class Foo(TypedDict): x: Required[int] y: NotRequired[str] ``` This is effectively equivalent to the following protocol: ```python class FooProto(Protocol): def __init__(self, x: int, y: str): ... def copy(self: Self) -> Self: ... def update(self: _T, __m: _T) -> None: ... def items(self) -> ItemsView[str, object]: ... def keys(self) -> KeysView[str]: ... def values(self) -> ValuesView[object]: ... @overload def get(self, k: Literal["x"]) -> int: ... @overload def get(self, k: Literal["x"], default: _T) -> int | _T: ... @overload def get(self, k: Literal["y"]) -> str: ... @overload def get(self, k: Literal["y"], default: _T) -> str | _T: ... @overload def setdefault(self, k: Literal["x"], default: int) -> int: ... @overload def setdefault(self, k: Literal["y"], default: str) -> str: ... @overload def pop(self, k: Literal["y"]) -> int: ... @overload def pop(self, k: Literal["y"], default: _T) -> int | _T: ... ``` Also, as Franek said, the flexibility of protocols make them unsuitable for `**kwargs` because the runtime type for `kwargs` is a dict, so its type must conform to the interface of a dict. That's what a TypedDict is designed to do. -- Eric Traut Contributor to pyright & pylance Microsoft