mypy
mypy copied to clipboard
Cannot turn existing dictionary into TypedDict
I expected any of these forms to be allowed by MyPy, but they aren't:
# typed_dict_test.py
from typing import TypedDict
class D(TypedDict):
a: str
b: int
d_untyped = {"a": "foo", "b": 1}
d0 = D({"a": "foo", "b": 1}) # The only one that works
print(d0)
d1 = D(d_untyped)
print(d1)
d2 = D(**d_untyped)
print(d2)
d3 = D(dict(d_untyped))
print(d3)
d4 = D(dict(**d_untyped))
print(d4)
$ python typed_dict_test.py
{'a': 'foo', 'b': 1}
{'a': 'foo', 'b': 1}
{'a': 'foo', 'b': 1}
{'a': 'foo', 'b': 1}
{'a': 'foo', 'b': 1}
$ python -V
Python 3.8.3
$ mypy typed_dict_test.py
typed_dict_test.py:13: error: Expected keyword arguments, {...}, or dict(...) in TypedDict constructor [misc]
d1 = D(d_untyped)
^
typed_dict_test.py:16: error: Expected keyword arguments, {...}, or dict(...) in TypedDict constructor [misc]
d2 = D(**d_untyped)
^
typed_dict_test.py:19: error: Expected keyword arguments, {...}, or dict(...) in TypedDict constructor [misc]
d3 = D(dict(d_untyped))
^
typed_dict_test.py:22: error: Expected keyword arguments, {...}, or dict(...) in TypedDict constructor [misc]
d4 = D(dict(**d_untyped))
^
Found 4 errors in 1 file (checked 1 source file)
$ cat mypy.ini
[mypy]
warn_redundant_casts = True
warn_unused_configs = True
pretty = True
show_error_codes = True
disallow_any_generics = True
disallow_subclassing_any = True
disallow_untyped_calls = True
disallow_incomplete_defs = True
check_untyped_defs = True
disallow_untyped_decorators = True
no_implicit_optional = True
warn_unused_ignores = True
warn_return_any = True
no_implicit_reexport = True
I checked that cast(D, ...)
works, but I wonder if this is the right way to proceed, or a workaround.
While this is difficult in principle if d_untyped
is not annotated (because it's inferred as Dict[str,object]
), the same errors occur even if it's hand-annotated as D
.
This gap makes it quite tough to do more complicated manipulations involving TypedDicts
since there's no easy way to express "adding keys to one typeddict to make a different typeddict".
This gap makes it quite tough to do more complicated manipulations involving
TypedDicts
since there's no easy way to express "adding keys to one typeddict to make a different typeddict".
Indeed, even the following doesn't work:
class D2(D):
c: str
d5a = D2(d0.copy(), c="bar")
d5b = D2(dict(d0.copy(), c="bar"))
d6a = D2(d0, c="bar")
d6b = D2(dict(d0, c="bar"))
If there's any workarounds for this - or even best practices - (perhaps not even using the constructor and using a TypeGuard
), feel free to mention as well (and post Python + mypy version)
I just stumbled over the same problem and used this as a solution:
import typing
class D(TypedDict):
a: str
b: int
d_untyped = {"a": "foo", "b": 1}
d_typed = typing.cast(D, d_untyped)
See also this blog post: https://adamj.eu/tech/2021/07/06/python-type-hints-how-to-use-typing-cast/
Don´t know, if that is the way to go for now and would be interested in other opinions, too :)