As a completely separate idea, it may be useful to make it easy to extract the extra field values from a runtime value of a TypedDict, and to explicitly copy those field values to a new TypedDict instance. Consider the following:
class Point2D(TypedDict): x: int y: int
class NamedPoint2D(Point2D): name: str
n_point = NamedPoint2D(x=1, y=2, name='Center')
P = TypeVar('P', Point2D)
# Does swap the "x" and "y" components of a Point2D, # while preserving its __extra__ fields. def transpose(p: P) -> P: return Point2D( # maybe write P( here instead? x=p['y'], y=p['x'], **Point2D.__extra__(p))
transposed_n_point = transpose(n_point) print(transposed_n_point) # prints: {"x": 2, "y": 1, "name": "Center"} reveal_type(transposed_n_point) # NamedPoint2D
The above code (1) uses a new @classmethod called __extra__() on the Point2D TypedDict that extracts any fields from the provided value that are not in Point2D's definition, and (2) allows a Point2D TypedDict to be constructed with a ** splat with statically-unknown extra fields.
I’d like to see some use cases for such syntax before we go there.
The "transpose" function above is a scenario. The related general use case is "I want to generate a derived version of a TypedDict instance, completely redefining all known field values, but also preserve any extra fields.". This "copy-on-write" pattern seems to be a relatively common in coding styles that avoid mutating data structures directly, such as in Clojure. To be fair the TypedDicts I use in my own code typically only exist for a short time before they are JSON-dumped or after they are JSON-parsed and then discarded (in a request-response cycle of a web app), so my own code doesn't typically have TypedDict instances that live long enough to make it useful to support fancy mutations on them.
And why would __extra__ need to be a Point2D method?
Well the implementation needs to be some kind of function that takes (1) the TypedDict instance and (2) the TypedDict type (like Point2D), because the type is erased from the instance at runtime. In particular you could *not* write: p.__extra__() # an instance method call because "p" is a dict at runtime rather than a Point2D. You *could* write a freestanding function like: typing.get_typeddict_extras(p, Point2D) but it seems more succinct to just make it a class method on Point2D: Point2D.__extras__(p) # use dunder to avoid clash with field names or equivalently: Point2D._get_extras(p) # use underscore to avoid clash with field names -- David Foster | Seattle, WA, USA Contributor to TypedDict, mypy, and Python's typing system