Resolve
August 10, 2026 · View on GitHub
Struct.resolve(data) and Signature.resolve(data) validate field by field,
reject unknown or missing keys, and fill missing defaults with fresh values.
They return a dictionary and do not construct dataclass instances from nested
dictionaries.
Validation reaches every depth; filling does not. A supplied value is checked
all the way down — inside nested dictionaries, lists and unions — while defaults
are served only for the keys missing at the level being resolved. A nested
dataclass dictionary keeps exactly the keys it arrived with, and build fills
that dataclass's own missing defaults while constructing it. The example below
shows the shape of that split.
from dataclasses import dataclass, field
from pytypehint import struct_of
@dataclass
class Page:
size: int = 20
@dataclass
class Query:
page: Page = field(default_factory=Page)
resolved = struct_of(Query).resolve({"page": {"size": 50}})
# {'page': {'size': 50}}
Use resolve when a wrapper must inspect validated data before construction—for
example, to move an uploaded file or inject request context. Most standalone
callers should use build.
For a dataclass union, resolve validates $type and preserves it. build
removes the discriminator when constructing the selected class. The same holds
for the $type/$value wrapper of build.md: resolve returns the
wrapper as it was given, and build unwraps it. Input dataclass instances are
rejected by both APIs.
resolve expects exact Python, like build. A tree that arrived in a portable
form goes through decode first; the two are separate calls, and
resolve never decodes anything on its own. A supplied value passes through
resolve by reference — it is validated, never transformed.