Reputation: 60564
I have a bunch of @dataclass
es and a bunch of corresponding TypedDict
s, and I want to facilitate smooth and type-checked conversion between them.
For example, consider
from dataclasses import dataclass
from typing_extensions import TypedDict
@dataclass
class Foo:
bar: int
baz: int
@property
def qux(self) -> int:
return self.bar + self.baz
class SerializedFoo(TypedDict):
bar: int
baz: int
qux: int
To create a serialization function, I can write something like
def serialize(foo: Foo) -> SerializedFoo:
return SerializedFoo(
bar=foo.bar,
baz=foo.baz,
qux=foo.qux,
)
but doing this for many types becomes tedious, and every time I update the types, I also have to update the serialization function.
I can also do something like
import dataclasses
def serialize(foo: Foo) -> SerializedFoo:
return SerializedFoo(**dataclasses.asdict(foo))
but this doesn't type check; mypy
complains that it Expected keyword arguments, {...}, or dict(...) in TypedDict constructor
.
Theoretically, it should be possible for a sufficiently smart type-checker to know that the dataclass has the properties needed to initialize the typed dictionary, but of course the usage of asdict
makes this impossible in practice.
Is there a better way to convert a dataclass
to a TypedDict
with corresponding fields, that lets me both have the type checker tell me when something is wrong, and not have to type out every field in the conversion?
Upvotes: 17
Views: 6392
Reputation: 61
TypedDict
is a regular dict at runtime, not a real class, doesn't do any type-checking, and is only for type hinting purposes. So, you can simply use typing.cast
(docs here):
import dataclasses
from typing import cast
def serialize(foo: Foo) -> SerializedFoo:
return cast(SerializedFoo, dataclasses.asdict(foo))
which will make Python type-checkers happy.
You could also check out the dacite
library. It has some nifty things for stuff like this without the overkill of marshmallow
. It allows some kind of type casting in its asdict
/from_dict
functions.
Upvotes: 6