Here are a couple of additional ideas to throw into the mix.
The first idea involves the use of a method declaration within a TypedDict.
Currently, TypedDict definitions don’t allow any method declarations. We could specify that a __closed__
method can be declared that returns the type of the allowed ‘extra’ attributes.
class Point(TypedDict):
x: float
y: float
def __closed__(self) -> Never: ...
This idea has the benefit of not affecting backward compatibility.
The downsides are that it’s probably not what most Python developers would expect. It’s also not clear how this would work with the functional form of TypedDict
.
The second idea involves the use of a new parameter to the TypedDict
constructor named closed
. If set to True
, the TypedDict
would be considered “closed” in that type checkers should assume that no additional items are allowed. If a user wants to allow additional items of a specific type, they would additionally include an __extra__: <type>
attribute declaration. If no __extra__
is present but closed
is set to True
, it acts as though an implicit __extra__: Never
is present.
class Point1(TypedDict, closed=True):
x: float
y: float
class Point2(TypedDict, closed=True):
x: float
y: float
__extra__: int
I think this construct feels pretty natural — especially in the common case where the user simply wants to specify that no other items can be present. It also preserves backward compatibility and works with the functional form. It could also be made to work with a potential future inlined version (by special-casing __extra__
in the inlined version).
In the (extremely unlikely) event that you want to create a closed TypedDict with a key named __extra__
, you could do so through inheritance.
# Create a non-closed TypedDict that includes `__extra__` item
class TDWithExtra(TypedDict):
x: float
y: float
__extra__: float
# Create a closed version of the above
class ClosedTDWithExtra1(TDWithExtra, closed=True):
...
# Or a closed version with extra items
class ClosedTDWithExtra2(TDWithExtra, closed=True):
__extra__: int