@@ -187,9 +187,10 @@ determining `supported and unsupported operations
187187The ``extra_items `` Class Parameter
188188-----------------------------------
189189
190- For a TypedDict type that specifies ``extra_items ``, during construction, the
191- value type of each unknown item is expected to be non-required and assignable
192- to the ``extra_items `` argument. For example::
190+ By default ``extra_items `` is unset. For a TypedDict type that specifies
191+ ``extra_items ``, during construction, the value type of each unknown item
192+ is expected to be non-required and assignable to the ``extra_items `` argument.
193+ For example::
193194
194195 class Movie(TypedDict, extra_items=bool):
195196 name: str
@@ -233,15 +234,14 @@ Here, ``'year'`` in ``a`` is an extra key defined on ``Movie`` whose value type
233234is ``int ``. ``'other_extra_key' `` in ``b `` is another extra key whose value type
234235must be assignable to the value of ``extra_items `` defined on ``MovieBase ``.
235236
236- ``extra_items `` is also supported with the functional syntax::
237-
238- Movie = TypedDict("Movie", {"name": str}, extra_items=int | None)
239-
240237.. _typed-dict-closed :
241238
242239The ``closed `` Class Parameter
243240------------------------------
244241
242+ When neither ``extra_items `` nor ``closed=True `` is specified, ``closed=False ``
243+ is assumed.
244+
245245When ``closed=True `` is set, no extra items are allowed. This is equivalent to
246246``extra_items=Never ``, because there can't be a value type that is assignable to
247247:class: `~typing.Never `. It is a runtime error to use the ``closed `` and
@@ -275,8 +275,11 @@ child class is also closed::
275275
276276As a consequence of ``closed=True `` being equivalent to ``extra_items=Never ``,
277277the same rules that apply to ``extra_items=Never `` also apply to
278- ``closed=True ``. It is possible to use ``closed=True `` when subclassing if the
279- ``extra_items `` argument is a read-only type::
278+ ``closed=True ``. While they both have the same effect, ``closed=True `` is
279+ preferred over ``extra_items=Never ``.
280+
281+ It is possible to use ``closed=True `` when subclassing if the ``extra_items ``
282+ argument is a read-only type::
280283
281284 class Movie(TypedDict, extra_items=ReadOnly[str]):
282285 pass
@@ -290,10 +293,12 @@ the same rules that apply to ``extra_items=Never`` also apply to
290293This will be further discussed in
291294:ref: `a later section <pep728-inheritance-read-only >`.
292295
293- When neither ``extra_items `` nor ``closed=True `` is specified, the TypedDict
294- is assumed to allow non-required extra items of value type ``ReadOnly[object] ``
295- during inheritance or assignability checks. This preserves the existing behavior
296- of TypedDict.
296+ The TypedDict should allow non-required extra items of value type
297+ ``ReadOnly[object] `` during inheritance or assignability checks, to
298+ preserve the default TypedDict behavior. Extra keys included in TypedDict
299+ object construction should still be caught, as mentioned in TypedDict's
300+ `typing spec
301+ <https://typing.python.org/en/latest/spec/typeddict.html#supported-and-unsupported-operations.> `__.
297302
298303``closed `` is also supported with the functional syntax::
299304
@@ -585,8 +590,33 @@ arguments of this type when constructed by calling the class object::
585590 year=2007,
586591 ) # Not OK. Extra items not allowed
587592
588- Interaction with Mapping[KT, VT]
589- --------------------------------
593+ Supported and Unsupported Operations
594+ ------------------------------------
595+
596+ This statement from the `typing spec
597+ <https://typing.python.org/en/latest/spec/typeddict.html#supported-and-unsupported-operations> `__
598+ still holds true.
599+
600+ Operations with arbitrary str keys (instead of string literals or other
601+ expressions with known string values) should generally be rejected.
602+
603+ This means that indexed accesses and assignments with arbitrary keys can still
604+ be rejected even when ``extra_items `` is specified.
605+
606+ Operations that already apply to ``NotRequired `` items should generally also
607+ apply to extra items, following the same rationale from the `typing spec
608+ <https://typing.python.org/en/latest/spec/typeddict.html#supported-and-unsupported-operations> `__:
609+
610+ The exact type checking rules are up to each type checker to decide. In some
611+ cases potentially unsafe operations may be accepted if the alternative is to
612+ generate false positive errors for idiomatic code.
613+
614+ Some operations are allowed due to the TypedDict being
615+ :term: `typing:assignable ` to ``Mapping[str, VT] `` or ``dict[str, VT] ``.
616+ The two following sections will expand on that.
617+
618+ Interaction with Mapping[str, VT]
619+ ---------------------------------
590620
591621A TypedDict type is :term: `typing:assignable ` to a type of the form ``Mapping[str, VT] ``
592622when all value types of the items in the TypedDict
@@ -618,12 +648,12 @@ and ``items()`` on such TypedDict types::
618648 reveal_type(movie.items()) # Revealed type is 'dict_items[str, str]'
619649 reveal_type(movie.values()) # Revealed type is 'dict_values[str, str]'
620650
621- Interaction with dict[KT , VT]
622- -----------------------------
651+ Interaction with dict[str , VT]
652+ ------------------------------
623653
624654Because the presence of ``extra_items `` on a closed TypedDict type
625655prohibits additional required keys in its :term: `typing:structural `
626- :term: `typing: subtypes <subtype> `, we can determine if the TypedDict type and
656+ :term: `subtypes <subtype> `, we can determine if the TypedDict type and
627657its structural subtypes will ever have any required key during static analysis.
628658
629659The TypedDict type is :term: `typing:assignable ` to ``dict[str, VT] `` if all
@@ -708,8 +738,25 @@ been removed in Python 3.13.
708738Because this is a type-checking feature, it can be made available to older
709739versions as long as the type checker supports it.
710740
711- Open Issues
712- ===========
741+ Rejected Ideas
742+ ==============
743+
744+ Use ``@final `` instead of ``closed `` Class Parameter
745+ -----------------------------------------------------
746+
747+ This was discussed `here <https://github.com/python/mypy/issues/7981 >`__.
748+
749+ Quoting a relevant `comment
750+ <https://github.com/python/mypy/issues/7981#issuecomment-2080161813> `__
751+ from Eric Traut:
752+
753+ The @final class decorator indicates that a class cannot be subclassed. This
754+ makes sense for classes that define nominal types. However, TypedDict is a
755+ structural type, similar to a Protocol. That means two TypedDict classes
756+ with different names but the same field definitions are equivalent types.
757+ Their names and hierarchies don't matter for determining type consistency.
758+ For that reason, @final has no impact on a TypedDict type consistency rules,
759+ nor should it change the behavior of items or values.
713760
714761Use a Special ``__extra_items__ `` Key with the ``closed `` Class Parameter
715762-------------------------------------------------------------------------
@@ -725,7 +772,7 @@ where ``closed=True`` is required for ``__extra_items__`` to be treated
725772specially, to avoid key collision.
726773
727774Some members of the community concern about the elegance of the syntax.
728- Practiaclly , the key collision with a regular key can be mitigated with
775+ Practically , the key collision with a regular key can be mitigated with
729776workarounds, but since using a reserved key is central to this proposal,
730777there are limited ways forward to address the concerns.
731778
@@ -767,9 +814,6 @@ types altogether, but there are some disadvantages. `For example
767814- The types don't appear in an annotation context, so their evaluation will
768815 not be deferred.
769816
770- Rejected Ideas
771- ==============
772-
773817Allowing Extra Items without Specifying the Type
774818------------------------------------------------
775819
@@ -827,19 +871,24 @@ For example:
827871 [index : string ]: number | string
828872 }
829873
830- This is a known limitation discussed in `TypeScript's issue tracker
831- <https://github.com/microsoft/TypeScript/issues/17867> `__,
832- where it is suggested that there should be a way to exclude the defined keys
833- from the index signature so that it is possible to define a type like
834- ``MovieWithExtraNumber ``.
874+ While this restriction allows for sound indexed accesses with arbitrary keys,
875+ it comes with usability limitations discussed in `TypeScript's issue tracker
876+ <https://github.com/microsoft/TypeScript/issues/17867> `__.
877+ A suggestion was to allow excluding the defined keys from the index signature,
878+ to define a type like ``MovieWithExtraNumber ``. This probably involves
879+ subtraction types, which is beyond the scope of this PEP.
835880
836881Reference Implementation
837882========================
838883
839- An earlier revision of proposal is supported in `pyright 1.1.352
840- <https://github.com/microsoft/pyright/releases/tag/1.1.352> `_, and `pyanalyze
884+ This is supported in `pyright 1.1.386
885+ <https://github.com/microsoft/pyright/releases/tag/1.1.386> `_, and an earlier
886+ revision is supported in `pyanalyze
8418870.12.0 <https://github.com/quora/pyanalyze/releases/tag/v0.12.0> `_.
842888
889+ This is also supported in `typing-extensions 4.13.0
890+ <https://pypi.org/project/typing-extensions/4.13.0/> `_.
891+
843892Acknowledgments
844893===============
845894
0 commit comments