I'm trying to define a "nested" TypedDict
as a set of inner classes, where SomeDict
dictionary is supposed to have an id
field which can be either yet another TypedDict
or None
:
from typing import TypedDict
class MyClass:
class SomeIdFieldDict(TypedDict):
some_nested_field: str
class SomeDict(TypedDict):
id: SomeIdFieldDict | None # The error is in this line.
The above code gives me the following error: NameError: name 'SomeIdFieldDict' is not defined
I've tried to quote the SomeIdFieldDict
, but now Python type checker treats it as a string:
from typing import TypedDict
class MyClass:
class SomeIdFieldDict(TypedDict):
some_nested_field: str
class SomeDict(TypedDict):
id: "SomeIdFieldDict" | None # The error is in this line.
With the above, I'm getting:
TypeError: unsupported operand type(s) for |: 'str' and 'NoneType'
I've also tried referencing to the top-level class, to no avail (getting the same error as above):
from typing import TypedDict
class MyClass:
class SomeIdFieldDict(TypedDict):
some_nested_field: str
class SomeDict(TypedDict):
id: "MyClass.SomeIdFieldDict" | None # The error is in this line.
Yet another approach I've tried taking was to define the id
type "inline", like so:
from typing import TypedDict
class MyClass:
class SomeDict(TypedDict):
id: TypedDict("SomeIdFieldDict", {"some_nested_field": str}) | None
... but it seems it isn't parsed correctly and the field is being treated as Any
, the type hints for id
field show as: id: Any | None
Is there any way to be able to define this sort of "nested" TypeDict
as inner classes?
You can either use typing.Optional["SomeIdFieldDict"]
instead or, what I would suggest, use __future__.annotations
like this:
from __future__ import annotations
from typing import TypedDict
class MyClass:
class SomeIdFieldDict(TypedDict):
some_nested_field: str
class SomeDict(TypedDict):
id: SomeIdFieldDict | None
def f(x: MyClass.SomeDict) -> None:
print(x)
if __name__ == '__main__':
f({"id": {"some_nested_field": "a"}}) # works
f({"foo": "bar"}) # error: Extra key "foo" for TypedDict "SomeDict"
This will pass/fail with mypy
as expected. However, I noticed that PyCharm mistakenly still complains about Unresolved reference 'SomeIdFieldDict'
. But that is a bug on their end.