Description
Proposal
It might be useful if there was a possibility to mark a TypedDict
as "sealed" (other naming suggestions welcome!), so that dicts with additional keys are not considered subtypes. E.g.:
from typing import TypedDict
class A(TypedDict, sealed=True):
a: int
class AB(TypedDict):
a: int
b: int
ab: AB = {"a": 1, "b": 2}
a: A = ab # error!
An alternative syntax may be to allow making any already-defined TypedDict
sealed by wrapping it in Sealed[...]
. E.g.:
class A(TypedDict):
a: int
a: Sealed[A] = ab # error!
Motivation
Situation
Consider trying to update()
a TypedDict
instance with an instance of a "partial" variant that only contains some of the former's keys:
from typing import TypedDict
class AB(TypedDict):
a: int
b: int
class A(TypedDict):
a: int
ab: AB = {"a": 1, "b": 2}
a: A = {"a": 3}
ab.update(a)
Current type checking behavior
Both major type checkers will complain about this:
Mypy | Pyright |
---|---|
error: Argument 1 to |
error: No overloads for |
As can be seen from the error messages, the reason is that A
allows subtypes which would have a key b
just like AB
, but the type of the value for that key could be different in these hypothetical subtypes from what it is in AB
.
Current workaround & why it's insufficient
This issue can be worked around by adding all additional keys of AB
as NotRequired
keys on A
:
from typing import NotRequired
class A(TypedDict):
a: int
b: NotRequired[int]
This will make ab.update(a)
pass type checking.
But that is quite hacky:
- If I have several different
TypedDict
s likeAB
, with different extra keys (beyond those inA
), I have to introduce separate variants ofA
for each of them just to makeupdate
work correctly. - If
AB
has many more keys thanA
, I have to repeat all of them. - "Philosophically", it doesn't seem right that
A
should have any knowledge ofAB
's keys just to make theupdate
operation work.
By contrast, if "sealed" TypedDict
s were possible, I would only need the one sealed A
and that's it.
Related proposals
@sealed
decorator- This has less in common with this proposal than the name might suggest and I only bring it up at all because the name is the same: The
@sealed
decorator would apply to classes and forbid subclasses beyond those found in the same module. Meanwhile, this proposal is only about individualTypedDict
s, shares none of the module-scoping logic, and can be seen as introducing a new kind of structural type, whereas@sealed
is about nominal types.
- This has less in common with this proposal than the name might suggest and I only bring it up at all because the name is the same: The