Convert dataclass to TypedDict for fun and type safety 🪄
uber- /ˈuːbə/ to a great or extreme degree
python>=3.11mypy>=1.0.1
pip install ubertypedAdd ubertyped.mypy_plugin to the list of plugins in your mypy config file
(for example pyproject.toml)
[tool.mypy]
python_version = "3.11"
plugins = ["ubertyped.mypy_plugin"]- ✅
AsTypedDict[T]generic type convertingdataclasses toTypeDicts - ✅
as_typed_dictutility function wrappingdataclasses.asdict - ✅ Support for usage with
TypeVars - ✅ Nested dataclasses
- ✅ Compatibility with other typecheckers such as
PylanceandPyright - ✅ Zero dependencies
from dataclasses import asdict, dataclass
from typing import Self, reveal_type
from ubertyped import AsTypedDict, as_typed_dict
@dataclass
class Base:
base: bool
@dataclass
class IntWrapper:
value: int
@dataclass
class Data(Base):
version: IntWrapper
command: str
def as_typed_dict(self) -> AsTypedDict[Self]:
return as_typed_dict(self)
data = Data(version=IntWrapper(1), command="c", base=False)
# 🎉 Type-safe conversion!
td = as_typed_dict(data)
reveal_type(td)
# Revealed type is "TypedDict({'base': builtins.bool, 'version': TypedDict({'value': builtins.int}), 'command': builtins.str})"
# 🎉 Works with nested dataclasses too!
reveal_type(td["version"]["value"])
# Revealed type is "builtins.int"
# 🎉 Binding `Self` in methods is resolved correctly!
reveal_type(data.as_typed_dict)
# Revealed type is "def () -> TypedDict({'base': builtins.bool, 'version': TypedDict({'value': builtins.int}), 'command': builtins.str})"
# 🎉 At runtime, `as_typed_dict` is just a wrapper around `dataclasses.asdict`!
if asdict(data) == data.as_typed_dict():
print("✅")
else:
print("❌")