CINXE.COM

PEP 728 – TypedDict with Typed Extra Items | peps.python.org

<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="color-scheme" content="light dark"> <title>PEP 728 – TypedDict with Typed Extra Items | peps.python.org</title> <link rel="shortcut icon" href="../_static/py.png"> <link rel="canonical" href="https://peps.python.org/pep-0728/"> <link rel="stylesheet" href="../_static/style.css" type="text/css"> <link rel="stylesheet" href="../_static/mq.css" type="text/css"> <link rel="stylesheet" href="../_static/pygments.css" type="text/css" media="(prefers-color-scheme: light)" id="pyg-light"> <link rel="stylesheet" href="../_static/pygments_dark.css" type="text/css" media="(prefers-color-scheme: dark)" id="pyg-dark"> <link rel="alternate" type="application/rss+xml" title="Latest PEPs" href="https://peps.python.org/peps.rss"> <meta property="og:title" content='PEP 728 – TypedDict with Typed Extra Items | peps.python.org'> <meta property="og:description" content="This PEP adds two class parameters, closed and extra_items to type the extra items on a TypedDict. This addresses the need to define closed TypedDict types or to type a subset of keys that might appear in a dict while permitting additional items of a sp..."> <meta property="og:type" content="website"> <meta property="og:url" content="https://peps.python.org/pep-0728/"> <meta property="og:site_name" content="Python Enhancement Proposals (PEPs)"> <meta property="og:image" content="https://peps.python.org/_static/og-image.png"> <meta property="og:image:alt" content="Python PEPs"> <meta property="og:image:width" content="200"> <meta property="og:image:height" content="200"> <meta name="description" content="This PEP adds two class parameters, closed and extra_items to type the extra items on a TypedDict. This addresses the need to define closed TypedDict types or to type a subset of keys that might appear in a dict while permitting additional items of a sp..."> <meta name="theme-color" content="#3776ab"> </head> <body> <svg xmlns="http://www.w3.org/2000/svg" style="display: none;"> <symbol id="svg-sun-half" viewBox="0 0 24 24" pointer-events="all"> <title>Following system colour scheme</title> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> <circle cx="12" cy="12" r="9"></circle> <path d="M12 3v18m0-12l4.65-4.65M12 14.3l7.37-7.37M12 19.6l8.85-8.85"></path> </svg> </symbol> <symbol id="svg-moon" viewBox="0 0 24 24" pointer-events="all"> <title>Selected dark colour scheme</title> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> <path stroke="none" d="M0 0h24v24H0z" fill="none"></path> <path d="M12 3c.132 0 .263 0 .393 0a7.5 7.5 0 0 0 7.92 12.446a9 9 0 1 1 -8.313 -12.454z"></path> </svg> </symbol> <symbol id="svg-sun" viewBox="0 0 24 24" pointer-events="all"> <title>Selected light colour scheme</title> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> <circle cx="12" cy="12" r="5"></circle> <line x1="12" y1="1" x2="12" y2="3"></line> <line x1="12" y1="21" x2="12" y2="23"></line> <line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line> <line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line> <line x1="1" y1="12" x2="3" y2="12"></line> <line x1="21" y1="12" x2="23" y2="12"></line> <line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line> <line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line> </svg> </symbol> </svg> <script> document.documentElement.dataset.colour_scheme = localStorage.getItem("colour_scheme") || "auto" </script> <section id="pep-page-section"> <header> <h1>Python Enhancement Proposals</h1> <ul class="breadcrumbs"> <li><a href="https://www.python.org/" title="The Python Programming Language">Python</a> &raquo; </li> <li><a href="../pep-0000/">PEP Index</a> &raquo; </li> <li>PEP 728</li> </ul> <button id="colour-scheme-cycler" onClick="setColourScheme(nextColourScheme())"> <svg aria-hidden="true" class="colour-scheme-icon-when-auto"><use href="#svg-sun-half"></use></svg> <svg aria-hidden="true" class="colour-scheme-icon-when-dark"><use href="#svg-moon"></use></svg> <svg aria-hidden="true" class="colour-scheme-icon-when-light"><use href="#svg-sun"></use></svg> <span class="visually-hidden">Toggle light / dark / auto colour theme</span> </button> </header> <article> <section id="pep-content"> <h1 class="page-title">PEP 728 – TypedDict with Typed Extra Items</h1> <dl class="rfc2822 field-list simple"> <dt class="field-odd">Author<span class="colon">:</span></dt> <dd class="field-odd">Zixuan James Li &lt;p359101898&#32;&#97;t&#32;gmail.com&gt;</dd> <dt class="field-even">Sponsor<span class="colon">:</span></dt> <dd class="field-even">Jelle Zijlstra &lt;jelle.zijlstra&#32;&#97;t&#32;gmail.com&gt;</dd> <dt class="field-odd">Discussions-To<span class="colon">:</span></dt> <dd class="field-odd"><a class="reference external" href="https://discuss.python.org/t/pep-728-typeddict-with-typed-extra-items/45443">Discourse thread</a></dd> <dt class="field-even">Status<span class="colon">:</span></dt> <dd class="field-even"><abbr title="Proposal under active discussion and revision">Draft</abbr></dd> <dt class="field-odd">Type<span class="colon">:</span></dt> <dd class="field-odd"><abbr title="Normative PEP with a new feature for Python, implementation change for CPython or interoperability standard for the ecosystem">Standards Track</abbr></dd> <dt class="field-even">Topic<span class="colon">:</span></dt> <dd class="field-even"><a class="reference external" href="../topic/typing/">Typing</a></dd> <dt class="field-odd">Created<span class="colon">:</span></dt> <dd class="field-odd">12-Sep-2023</dd> <dt class="field-even">Python-Version<span class="colon">:</span></dt> <dd class="field-even">3.14</dd> <dt class="field-odd">Post-History<span class="colon">:</span></dt> <dd class="field-odd"><a class="reference external" href="https://discuss.python.org/t/pep-728-typeddict-with-typed-extra-items/45443" title="Discourse thread">09-Feb-2024</a></dd> </dl> <hr class="docutils" /> <section id="contents"> <details><summary>Table of Contents</summary><ul class="simple"> <li><a class="reference internal" href="#abstract">Abstract</a></li> <li><a class="reference internal" href="#motivation">Motivation</a><ul> <li><a class="reference internal" href="#disallowing-extra-items-explicitly">Disallowing Extra Items Explicitly</a></li> <li><a class="reference internal" href="#allowing-extra-items-of-a-certain-type">Allowing Extra Items of a Certain Type</a></li> <li><a class="reference internal" href="#support-additional-keys-for-unpack">Support Additional Keys for <code class="docutils literal notranslate"><span class="pre">Unpack</span></code></a></li> </ul> </li> <li><a class="reference internal" href="#rationale">Rationale</a></li> <li><a class="reference internal" href="#specification">Specification</a><ul> <li><a class="reference internal" href="#the-extra-items-class-parameter">The <code class="docutils literal notranslate"><span class="pre">extra_items</span></code> Class Parameter</a></li> <li><a class="reference internal" href="#the-closed-class-parameter">The <code class="docutils literal notranslate"><span class="pre">closed</span></code> Class Parameter</a></li> <li><a class="reference internal" href="#interaction-with-totality">Interaction with Totality</a></li> <li><a class="reference internal" href="#interaction-with-unpack">Interaction with <code class="docutils literal notranslate"><span class="pre">Unpack</span></code></a></li> <li><a class="reference internal" href="#interaction-with-read-only-items">Interaction with Read-only Items</a></li> <li><a class="reference internal" href="#inheritance">Inheritance</a></li> <li><a class="reference internal" href="#assignability">Assignability</a></li> <li><a class="reference internal" href="#interaction-with-constructors">Interaction with Constructors</a></li> <li><a class="reference internal" href="#interaction-with-mapping-kt-vt">Interaction with Mapping[KT, VT]</a></li> <li><a class="reference internal" href="#interaction-with-dict-kt-vt">Interaction with dict[KT, VT]</a></li> <li><a class="reference internal" href="#runtime-behavior">Runtime behavior</a></li> </ul> </li> <li><a class="reference internal" href="#how-to-teach-this">How to Teach This</a></li> <li><a class="reference internal" href="#backwards-compatibility">Backwards Compatibility</a></li> <li><a class="reference internal" href="#open-issues">Open Issues</a><ul> <li><a class="reference internal" href="#use-a-special-extra-items-key-with-the-closed-class-parameter">Use a Special <code class="docutils literal notranslate"><span class="pre">__extra_items__</span></code> Key with the <code class="docutils literal notranslate"><span class="pre">closed</span></code> Class Parameter</a></li> <li><a class="reference internal" href="#support-a-new-syntax-of-specifying-keys">Support a New Syntax of Specifying Keys</a></li> </ul> </li> <li><a class="reference internal" href="#rejected-ideas">Rejected Ideas</a><ul> <li><a class="reference internal" href="#allowing-extra-items-without-specifying-the-type">Allowing Extra Items without Specifying the Type</a></li> <li><a class="reference internal" href="#support-extra-items-with-intersection">Support Extra Items with Intersection</a></li> <li><a class="reference internal" href="#requiring-type-compatibility-of-the-known-items-with-extra-items">Requiring Type Compatibility of the Known Items with <code class="docutils literal notranslate"><span class="pre">extra_items</span></code></a></li> </ul> </li> <li><a class="reference internal" href="#reference-implementation">Reference Implementation</a></li> <li><a class="reference internal" href="#acknowledgments">Acknowledgments</a></li> <li><a class="reference internal" href="#copyright">Copyright</a></li> </ul> </details></section> <section id="abstract"> <h2><a class="toc-backref" href="#abstract" role="doc-backlink">Abstract</a></h2> <p>This PEP adds two class parameters, <code class="docutils literal notranslate"><span class="pre">closed</span></code> and <code class="docutils literal notranslate"><span class="pre">extra_items</span></code> to type the extra items on a <a class="reference external" href="https://docs.python.org/3/library/typing.html#typing.TypedDict" title="(in Python v3.13)"><code class="xref py py-class docutils literal notranslate"><span class="pre">TypedDict</span></code></a>. This addresses the need to define closed TypedDict types or to type a subset of keys that might appear in a <code class="docutils literal notranslate"><span class="pre">dict</span></code> while permitting additional items of a specified type.</p> </section> <section id="motivation"> <h2><a class="toc-backref" href="#motivation" role="doc-backlink">Motivation</a></h2> <p>A <a class="reference external" href="https://docs.python.org/3/library/typing.html#typing.TypedDict" title="(in Python v3.13)"><code class="xref py py-class docutils literal notranslate"><span class="pre">typing.TypedDict</span></code></a> type can annotate the value type of each known item in a dictionary. However, due to <a class="reference external" href="https://typing.python.org/en/latest/spec/glossary.html#term-structural" title="(in typing)"><span>structural</span></a> <a class="reference external" href="https://typing.python.org/en/latest/spec/glossary.html#term-assignable" title="(in typing)"><span class="xref std std-term">assignability</span></a>, a TypedDict can have extra items that are not visible through its type. There is currently no way to restrict the types of items that might be present in the TypedDict type’s <a class="reference external" href="https://typing.python.org/en/latest/spec/glossary.html#term-consistent-subtype" title="(in typing)"><span class="xref std std-term">consistent subtypes</span></a>.</p> <section id="disallowing-extra-items-explicitly"> <h3><a class="toc-backref" href="#disallowing-extra-items-explicitly" role="doc-backlink">Disallowing Extra Items Explicitly</a></h3> <p>The current behavior of TypedDict prevents users from defining a TypedDict type when it is expected that the type contains no extra items.</p> <p>Due to the possible presence of extra items, type checkers cannot infer more precise return types for <code class="docutils literal notranslate"><span class="pre">.items()</span></code> and <code class="docutils literal notranslate"><span class="pre">.values()</span></code> on a TypedDict. This can also be resolved by <a class="reference external" href="https://github.com/python/mypy/issues/7981">defining a closed TypedDict type</a>.</p> <p>Another possible use case for this is a sound way to <a class="reference external" href="https://github.com/python/mypy/issues/9953">enable type narrowing</a> with the <code class="docutils literal notranslate"><span class="pre">in</span></code> check:</p> <div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">class</span><span class="w"> </span><span class="nc">Movie</span><span class="p">(</span><span class="n">TypedDict</span><span class="p">):</span> <span class="n">name</span><span class="p">:</span> <span class="nb">str</span> <span class="n">director</span><span class="p">:</span> <span class="nb">str</span> <span class="k">class</span><span class="w"> </span><span class="nc">Book</span><span class="p">(</span><span class="n">TypedDict</span><span class="p">):</span> <span class="n">name</span><span class="p">:</span> <span class="nb">str</span> <span class="n">author</span><span class="p">:</span> <span class="nb">str</span> <span class="k">def</span><span class="w"> </span><span class="nf">fun</span><span class="p">(</span><span class="n">entry</span><span class="p">:</span> <span class="n">Movie</span> <span class="o">|</span> <span class="n">Book</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kc">None</span><span class="p">:</span> <span class="k">if</span> <span class="s2">&quot;author&quot;</span> <span class="ow">in</span> <span class="n">entry</span><span class="p">:</span> <span class="n">reveal_type</span><span class="p">(</span><span class="n">entry</span><span class="p">)</span> <span class="c1"># Revealed type is still &#39;Movie | Book&#39;</span> </pre></div> </div> <p>Nothing prevents a <code class="docutils literal notranslate"><span class="pre">dict</span></code> that is assignable with <code class="docutils literal notranslate"><span class="pre">Movie</span></code> to have the <code class="docutils literal notranslate"><span class="pre">author</span></code> key, and under the current specification it would be incorrect for the type checker to <a class="reference external" href="https://typing.python.org/en/latest/spec/glossary.html#term-narrow" title="(in typing)"><span>narrow</span></a> its type.</p> </section> <section id="allowing-extra-items-of-a-certain-type"> <h3><a class="toc-backref" href="#allowing-extra-items-of-a-certain-type" role="doc-backlink">Allowing Extra Items of a Certain Type</a></h3> <p>For supporting API interfaces or legacy codebase where only a subset of possible keys are known, it would be useful to explicitly specify extra items of certain value types.</p> <p>However, the typing spec is more restrictive when checking the construction of a TypedDict, <a class="reference external" href="https://github.com/python/mypy/issues/4617">preventing users</a> from doing this:</p> <div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">class</span><span class="w"> </span><span class="nc">MovieBase</span><span class="p">(</span><span class="n">TypedDict</span><span class="p">):</span> <span class="n">name</span><span class="p">:</span> <span class="nb">str</span> <span class="k">def</span><span class="w"> </span><span class="nf">foo</span><span class="p">(</span><span class="n">movie</span><span class="p">:</span> <span class="n">MovieBase</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kc">None</span><span class="p">:</span> <span class="c1"># movie can have extra items that are not visible through MovieBase</span> <span class="o">...</span> <span class="n">movie</span><span class="p">:</span> <span class="n">MovieBase</span> <span class="o">=</span> <span class="p">{</span><span class="s2">&quot;name&quot;</span><span class="p">:</span> <span class="s2">&quot;Blade Runner&quot;</span><span class="p">,</span> <span class="s2">&quot;year&quot;</span><span class="p">:</span> <span class="mi">1982</span><span class="p">}</span> <span class="c1"># Not OK</span> <span class="n">foo</span><span class="p">({</span><span class="s2">&quot;name&quot;</span><span class="p">:</span> <span class="s2">&quot;Blade Runner&quot;</span><span class="p">,</span> <span class="s2">&quot;year&quot;</span><span class="p">:</span> <span class="mi">1982</span><span class="p">})</span> <span class="c1"># Not OK</span> </pre></div> </div> <p>While the restriction is enforced when constructing a TypedDict, due to <a class="reference external" href="https://typing.python.org/en/latest/spec/glossary.html#term-structural" title="(in typing)"><span>structural</span></a> <a class="reference external" href="https://typing.python.org/en/latest/spec/glossary.html#term-assignable" title="(in typing)"><span class="xref std std-term">assignability</span></a>, the TypedDict may have extra items that are not visible through its type. For example:</p> <div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">class</span><span class="w"> </span><span class="nc">Movie</span><span class="p">(</span><span class="n">MovieBase</span><span class="p">):</span> <span class="n">year</span><span class="p">:</span> <span class="nb">int</span> <span class="n">movie</span><span class="p">:</span> <span class="n">Movie</span> <span class="o">=</span> <span class="p">{</span><span class="s2">&quot;name&quot;</span><span class="p">:</span> <span class="s2">&quot;Blade Runner&quot;</span><span class="p">,</span> <span class="s2">&quot;year&quot;</span><span class="p">:</span> <span class="mi">1982</span><span class="p">}</span> <span class="n">foo</span><span class="p">(</span><span class="n">movie</span><span class="p">)</span> <span class="c1"># OK</span> </pre></div> </div> <p>It is not possible to acknowledge the existence of the extra items through <code class="docutils literal notranslate"><span class="pre">in</span></code> checks and access them without breaking type safety, even though they might exist from some <a class="reference external" href="https://typing.python.org/en/latest/spec/glossary.html#term-consistent-subtype" title="(in typing)"><span class="xref std std-term">consistent subtypes</span></a> of <code class="docutils literal notranslate"><span class="pre">MovieBase</span></code>:</p> <div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">def</span><span class="w"> </span><span class="nf">bar</span><span class="p">(</span><span class="n">movie</span><span class="p">:</span> <span class="n">MovieBase</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kc">None</span><span class="p">:</span> <span class="k">if</span> <span class="s2">&quot;year&quot;</span> <span class="ow">in</span> <span class="n">movie</span><span class="p">:</span> <span class="n">reveal_type</span><span class="p">(</span><span class="n">movie</span><span class="p">[</span><span class="s2">&quot;year&quot;</span><span class="p">])</span> <span class="c1"># Error: TypedDict &#39;MovieBase&#39; has no key &#39;year&#39;</span> </pre></div> </div> <p>Some workarounds have already been implemented to allow extra items, but none of them is ideal. For mypy, <code class="docutils literal notranslate"><span class="pre">--disable-error-code=typeddict-unknown-key</span></code> <a class="reference external" href="https://github.com/python/mypy/pull/14225">suppresses type checking error</a> specifically for unknown keys on TypedDict. This sacrifices type safety over flexibility, and it does not offer a way to specify that the TypedDict type expects additional keys whose value types are assignable with a certain type.</p> </section> <section id="support-additional-keys-for-unpack"> <h3><a class="toc-backref" href="#support-additional-keys-for-unpack" role="doc-backlink">Support Additional Keys for <code class="docutils literal notranslate"><span class="pre">Unpack</span></code></a></h3> <p><a class="pep reference internal" href="../pep-0692/" title="PEP 692 – Using TypedDict for more precise **kwargs typing">PEP 692</a> adds a way to precisely annotate the types of individual keyword arguments represented by <code class="docutils literal notranslate"><span class="pre">**kwargs</span></code> using TypedDict with <code class="docutils literal notranslate"><span class="pre">Unpack</span></code>. However, because TypedDict cannot be defined to accept arbitrary extra items, it is not possible to <a class="reference external" href="https://discuss.python.org/t/pep-692-using-typeddict-for-more-precise-kwargs-typing/17314/87">allow additional keyword arguments</a> that are not known at the time the TypedDict is defined.</p> <p>Given the usage of pre-<a class="pep reference internal" href="../pep-0692/" title="PEP 692 – Using TypedDict for more precise **kwargs typing">PEP 692</a> type annotation for <code class="docutils literal notranslate"><span class="pre">**kwargs</span></code> in existing codebases, it will be valuable to accept and type extra items on TypedDict so that the old typing behavior can be supported in combination with <code class="docutils literal notranslate"><span class="pre">Unpack</span></code>.</p> </section> </section> <section id="rationale"> <h2><a class="toc-backref" href="#rationale" role="doc-backlink">Rationale</a></h2> <p>A type that allows extra items of type <code class="docutils literal notranslate"><span class="pre">str</span></code> on a TypedDict can be loosely described as the intersection between the TypedDict and <code class="docutils literal notranslate"><span class="pre">Mapping[str,</span> <span class="pre">str]</span></code>.</p> <p><a class="reference external" href="https://www.typescriptlang.org/docs/handbook/2/objects.html#index-signatures">Index Signatures</a> in TypeScript achieve this:</p> <div class="highlight-typescript notranslate"><div class="highlight"><pre><span></span><span class="kr">type</span><span class="w"> </span><span class="nx">Foo</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">{</span> <span class="w"> </span><span class="nx">a</span><span class="o">:</span><span class="w"> </span><span class="kt">string</span> <span class="w"> </span><span class="p">[</span><span class="nx">key</span><span class="o">:</span><span class="w"> </span><span class="kt">string</span><span class="p">]</span><span class="o">:</span><span class="w"> </span><span class="kt">string</span> <span class="p">}</span> </pre></div> </div> <p>This proposal aims to support a similar feature without introducing general intersection of types or syntax changes, offering a natural extension to the existing assignability rules.</p> <p>We propose to add a class parameter <code class="docutils literal notranslate"><span class="pre">extra_items</span></code> to TypedDict. It accepts a <a class="reference external" href="https://typing.python.org/en/latest/spec/glossary.html#term-type-expression" title="(in typing)"><span>type expression</span></a> as the argument; when it is present, extra items are allowed, and their value types must be assignable to the type expression value.</p> <p>An application of this is to disallow extra items. We propose to add a <code class="docutils literal notranslate"><span class="pre">closed</span></code> class parameter, which only accepts a literal <code class="docutils literal notranslate"><span class="pre">True</span></code> or <code class="docutils literal notranslate"><span class="pre">False</span></code> as the argument. It should be a runtime error when <code class="docutils literal notranslate"><span class="pre">closed</span></code> and <code class="docutils literal notranslate"><span class="pre">extra_items</span></code> are used at the same time.</p> <p>Different from index signatures, the types of the known items do not need to be assignable to the <code class="docutils literal notranslate"><span class="pre">extra_items</span></code> argument.</p> <p>There are some advantages to this approach:</p> <ul class="simple"> <li>We can build on top of the <a class="reference external" href="https://typing.python.org/en/latest/spec/typeddict.html#assignability">assignability rules defined in the typing spec</a>, where <code class="docutils literal notranslate"><span class="pre">extra_items</span></code> can be treated as a pseudo-item.</li> <li>There is no need to introduce a grammar change to specify the type of the extra items.</li> <li>We can precisely type the extra items without requiring the value types of the known items to be <a class="reference external" href="https://typing.python.org/en/latest/spec/glossary.html#term-assignable" title="(in typing)"><span>assignable</span></a> to <code class="docutils literal notranslate"><span class="pre">extra_items</span></code>.</li> <li>We do not lose backwards compatibility as both <code class="docutils literal notranslate"><span class="pre">extra_items</span></code> and <code class="docutils literal notranslate"><span class="pre">closed</span></code> are opt-in only features.</li> </ul> </section> <section id="specification"> <h2><a class="toc-backref" href="#specification" role="doc-backlink">Specification</a></h2> <p>This specification is structured to parallel <a class="pep reference internal" href="../pep-0589/" title="PEP 589 – TypedDict: Type Hints for Dictionaries with a Fixed Set of Keys">PEP 589</a> to highlight changes to the original TypedDict specification.</p> <p>If <code class="docutils literal notranslate"><span class="pre">extra_items</span></code> is specified, extra items are treated as <a class="reference external" href="https://typing.python.org/en/latest/spec/typeddict.html#required-notrequired" title="(in typing)"><span class="xref std std-ref">non-required</span></a> items matching the <code class="docutils literal notranslate"><span class="pre">extra_items</span></code> argument, whose keys are allowed when determining <a class="reference external" href="https://typing.python.org/en/latest/spec/typeddict.html#supported-and-unsupported-operations">supported and unsupported operations</a>.</p> <section id="the-extra-items-class-parameter"> <h3><a class="toc-backref" href="#the-extra-items-class-parameter" role="doc-backlink">The <code class="docutils literal notranslate"><span class="pre">extra_items</span></code> Class Parameter</a></h3> <p>For a TypedDict type that specifies <code class="docutils literal notranslate"><span class="pre">extra_items</span></code>, during construction, the value type of each unknown item is expected to be non-required and assignable to the <code class="docutils literal notranslate"><span class="pre">extra_items</span></code> argument. For example:</p> <div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">class</span><span class="w"> </span><span class="nc">Movie</span><span class="p">(</span><span class="n">TypedDict</span><span class="p">,</span> <span class="n">extra_items</span><span class="o">=</span><span class="nb">bool</span><span class="p">):</span> <span class="n">name</span><span class="p">:</span> <span class="nb">str</span> <span class="n">a</span><span class="p">:</span> <span class="n">Movie</span> <span class="o">=</span> <span class="p">{</span><span class="s2">&quot;name&quot;</span><span class="p">:</span> <span class="s2">&quot;Blade Runner&quot;</span><span class="p">,</span> <span class="s2">&quot;novel_adaptation&quot;</span><span class="p">:</span> <span class="kc">True</span><span class="p">}</span> <span class="c1"># OK</span> <span class="n">b</span><span class="p">:</span> <span class="n">Movie</span> <span class="o">=</span> <span class="p">{</span> <span class="s2">&quot;name&quot;</span><span class="p">:</span> <span class="s2">&quot;Blade Runner&quot;</span><span class="p">,</span> <span class="s2">&quot;year&quot;</span><span class="p">:</span> <span class="mi">1982</span><span class="p">,</span> <span class="c1"># Not OK. &#39;int&#39; is not assignable to &#39;bool&#39;</span> <span class="p">}</span> </pre></div> </div> <p>Here, <code class="docutils literal notranslate"><span class="pre">extra_items=bool</span></code> specifies that items other than <code class="docutils literal notranslate"><span class="pre">'name'</span></code> have a value type of <code class="docutils literal notranslate"><span class="pre">bool</span></code> and are non-required.</p> <p>The alternative inline syntax is also supported:</p> <div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">Movie</span> <span class="o">=</span> <span class="n">TypedDict</span><span class="p">(</span><span class="s2">&quot;Movie&quot;</span><span class="p">,</span> <span class="p">{</span><span class="s2">&quot;name&quot;</span><span class="p">:</span> <span class="nb">str</span><span class="p">},</span> <span class="n">extra_items</span><span class="o">=</span><span class="nb">bool</span><span class="p">)</span> </pre></div> </div> <p>Accessing extra items is allowed. Type checkers must infer their value type from the <code class="docutils literal notranslate"><span class="pre">extra_items</span></code> argument:</p> <div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">def</span><span class="w"> </span><span class="nf">f</span><span class="p">(</span><span class="n">movie</span><span class="p">:</span> <span class="n">Movie</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kc">None</span><span class="p">:</span> <span class="n">reveal_type</span><span class="p">(</span><span class="n">movie</span><span class="p">[</span><span class="s2">&quot;name&quot;</span><span class="p">])</span> <span class="c1"># Revealed type is &#39;str&#39;</span> <span class="n">reveal_type</span><span class="p">(</span><span class="n">movie</span><span class="p">[</span><span class="s2">&quot;novel_adaptation&quot;</span><span class="p">])</span> <span class="c1"># Revealed type is &#39;bool&#39;</span> </pre></div> </div> <p><code class="docutils literal notranslate"><span class="pre">extra_items</span></code> is inherited through subclassing:</p> <div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">class</span><span class="w"> </span><span class="nc">MovieBase</span><span class="p">(</span><span class="n">TypedDict</span><span class="p">,</span> <span class="n">extra_items</span><span class="o">=</span><span class="nb">int</span> <span class="o">|</span> <span class="kc">None</span><span class="p">):</span> <span class="n">name</span><span class="p">:</span> <span class="nb">str</span> <span class="k">class</span><span class="w"> </span><span class="nc">Movie</span><span class="p">(</span><span class="n">MovieBase</span><span class="p">):</span> <span class="n">year</span><span class="p">:</span> <span class="nb">int</span> <span class="n">a</span><span class="p">:</span> <span class="n">Movie</span> <span class="o">=</span> <span class="p">{</span><span class="s2">&quot;name&quot;</span><span class="p">:</span> <span class="s2">&quot;Blade Runner&quot;</span><span class="p">,</span> <span class="s2">&quot;year&quot;</span><span class="p">:</span> <span class="kc">None</span><span class="p">}</span> <span class="c1"># Not OK. &#39;None&#39; is incompatible with &#39;int&#39;</span> <span class="n">b</span><span class="p">:</span> <span class="n">Movie</span> <span class="o">=</span> <span class="p">{</span> <span class="s2">&quot;name&quot;</span><span class="p">:</span> <span class="s2">&quot;Blade Runner&quot;</span><span class="p">,</span> <span class="s2">&quot;year&quot;</span><span class="p">:</span> <span class="mi">1982</span><span class="p">,</span> <span class="s2">&quot;other_extra_key&quot;</span><span class="p">:</span> <span class="kc">None</span><span class="p">,</span> <span class="p">}</span> <span class="c1"># OK</span> </pre></div> </div> <p>Here, <code class="docutils literal notranslate"><span class="pre">'year'</span></code> in <code class="docutils literal notranslate"><span class="pre">a</span></code> is an extra key defined on <code class="docutils literal notranslate"><span class="pre">Movie</span></code> whose value type is <code class="docutils literal notranslate"><span class="pre">int</span></code>. <code class="docutils literal notranslate"><span class="pre">'other_extra_key'</span></code> in <code class="docutils literal notranslate"><span class="pre">b</span></code> is another extra key whose value type must be assignable to the value of <code class="docutils literal notranslate"><span class="pre">extra_items</span></code> defined on <code class="docutils literal notranslate"><span class="pre">MovieBase</span></code>.</p> <p><code class="docutils literal notranslate"><span class="pre">extra_items</span></code> is also supported with the functional syntax:</p> <div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">Movie</span> <span class="o">=</span> <span class="n">TypedDict</span><span class="p">(</span><span class="s2">&quot;Movie&quot;</span><span class="p">,</span> <span class="p">{</span><span class="s2">&quot;name&quot;</span><span class="p">:</span> <span class="nb">str</span><span class="p">},</span> <span class="n">extra_items</span><span class="o">=</span><span class="nb">int</span> <span class="o">|</span> <span class="kc">None</span><span class="p">)</span> </pre></div> </div> </section> <section id="the-closed-class-parameter"> <span id="typed-dict-closed"></span><h3><a class="toc-backref" href="#the-closed-class-parameter" role="doc-backlink">The <code class="docutils literal notranslate"><span class="pre">closed</span></code> Class Parameter</a></h3> <p>When <code class="docutils literal notranslate"><span class="pre">closed=True</span></code> is set, no extra items are allowed. This is equivalent to <code class="docutils literal notranslate"><span class="pre">extra_items=Never</span></code>, because there can’t be a value type that is assignable to <code class="xref py py-class docutils literal notranslate"><span class="pre">Never</span></code>. It is a runtime error to use the <code class="docutils literal notranslate"><span class="pre">closed</span></code> and <code class="docutils literal notranslate"><span class="pre">extra_items</span></code> parameters in the same TypedDict definition.</p> <p>Similar to <code class="docutils literal notranslate"><span class="pre">total</span></code>, only a literal <code class="docutils literal notranslate"><span class="pre">True</span></code> or <code class="docutils literal notranslate"><span class="pre">False</span></code> is supported as the value of the <code class="docutils literal notranslate"><span class="pre">closed</span></code> argument. Type checkers should reject any non-literal value.</p> <p>Passing <code class="docutils literal notranslate"><span class="pre">closed=False</span></code> explicitly requests the default TypedDict behavior, where arbitrary other keys may be present and subclasses may add arbitrary items. It is a type checker error to pass <code class="docutils literal notranslate"><span class="pre">closed=False</span></code> if a superclass has <code class="docutils literal notranslate"><span class="pre">closed=True</span></code> or sets <code class="docutils literal notranslate"><span class="pre">extra_items</span></code>.</p> <p>If <code class="docutils literal notranslate"><span class="pre">closed</span></code> is not provided, the behavior is inherited from the superclass. If the superclass is TypedDict itself or the superclass does not have <code class="docutils literal notranslate"><span class="pre">closed=True</span></code> or the <code class="docutils literal notranslate"><span class="pre">extra_items</span></code> parameter, the previous TypedDict behavior is preserved: arbitrary extra items are allowed. If the superclass has <code class="docutils literal notranslate"><span class="pre">closed=True</span></code>, the child class is also closed:</p> <div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">class</span><span class="w"> </span><span class="nc">BaseMovie</span><span class="p">(</span><span class="n">TypedDict</span><span class="p">,</span> <span class="n">closed</span><span class="o">=</span><span class="kc">True</span><span class="p">):</span> <span class="n">name</span><span class="p">:</span> <span class="nb">str</span> <span class="k">class</span><span class="w"> </span><span class="nc">MovieA</span><span class="p">(</span><span class="n">BaseMovie</span><span class="p">):</span> <span class="c1"># OK, still closed</span> <span class="k">pass</span> <span class="k">class</span><span class="w"> </span><span class="nc">MovieB</span><span class="p">(</span><span class="n">BaseMovie</span><span class="p">,</span> <span class="n">closed</span><span class="o">=</span><span class="kc">True</span><span class="p">):</span> <span class="c1"># OK, but redundant</span> <span class="k">pass</span> <span class="k">class</span><span class="w"> </span><span class="nc">MovieC</span><span class="p">(</span><span class="n">BaseMovie</span><span class="p">,</span> <span class="n">closed</span><span class="o">=</span><span class="kc">False</span><span class="p">):</span> <span class="c1"># Type checker error</span> <span class="k">pass</span> </pre></div> </div> <p>As a consequence of <code class="docutils literal notranslate"><span class="pre">closed=True</span></code> being equivalent to <code class="docutils literal notranslate"><span class="pre">extra_items=Never</span></code>, the same rules that apply to <code class="docutils literal notranslate"><span class="pre">extra_items=Never</span></code> also apply to <code class="docutils literal notranslate"><span class="pre">closed=True</span></code>. It is possible to use <code class="docutils literal notranslate"><span class="pre">closed=True</span></code> when subclassing if the <code class="docutils literal notranslate"><span class="pre">extra_items</span></code> argument is a read-only type:</p> <div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">class</span><span class="w"> </span><span class="nc">Movie</span><span class="p">(</span><span class="n">TypedDict</span><span class="p">,</span> <span class="n">extra_items</span><span class="o">=</span><span class="n">ReadOnly</span><span class="p">[</span><span class="nb">str</span><span class="p">]):</span> <span class="k">pass</span> <span class="k">class</span><span class="w"> </span><span class="nc">MovieClosed</span><span class="p">(</span><span class="n">Movie</span><span class="p">,</span> <span class="n">closed</span><span class="o">=</span><span class="kc">True</span><span class="p">):</span> <span class="c1"># OK</span> <span class="k">pass</span> <span class="k">class</span><span class="w"> </span><span class="nc">MovieNever</span><span class="p">(</span><span class="n">Movie</span><span class="p">,</span> <span class="n">extra_items</span><span class="o">=</span><span class="n">Never</span><span class="p">):</span> <span class="c1"># OK, but &#39;closed=True&#39; is preferred</span> <span class="k">pass</span> </pre></div> </div> <p>This will be further discussed in <a class="reference internal" href="#pep728-inheritance-read-only"><span class="std std-ref">a later section</span></a>.</p> <p>When neither <code class="docutils literal notranslate"><span class="pre">extra_items</span></code> nor <code class="docutils literal notranslate"><span class="pre">closed=True</span></code> is specified, the TypedDict is assumed to allow non-required extra items of value type <code class="docutils literal notranslate"><span class="pre">ReadOnly[object]</span></code> during inheritance or assignability checks. This preserves the existing behavior of TypedDict.</p> <p><code class="docutils literal notranslate"><span class="pre">closed</span></code> is also supported with the functional syntax:</p> <div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">Movie</span> <span class="o">=</span> <span class="n">TypedDict</span><span class="p">(</span><span class="s2">&quot;Movie&quot;</span><span class="p">,</span> <span class="p">{</span><span class="s2">&quot;name&quot;</span><span class="p">:</span> <span class="nb">str</span><span class="p">},</span> <span class="n">closed</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span> </pre></div> </div> </section> <section id="interaction-with-totality"> <h3><a class="toc-backref" href="#interaction-with-totality" role="doc-backlink">Interaction with Totality</a></h3> <p>It is an error to use <code class="docutils literal notranslate"><span class="pre">Required[]</span></code> or <code class="docutils literal notranslate"><span class="pre">NotRequired[]</span></code> with <code class="docutils literal notranslate"><span class="pre">extra_items</span></code>. <code class="docutils literal notranslate"><span class="pre">total=False</span></code> and <code class="docutils literal notranslate"><span class="pre">total=True</span></code> have no effect on <code class="docutils literal notranslate"><span class="pre">extra_items</span></code> itself.</p> <p>The extra items are non-required, regardless of the <a class="reference external" href="https://typing.python.org/en/latest/spec/typeddict.html#totality">totality</a> of the TypedDict. <a class="reference external" href="https://typing.python.org/en/latest/spec/typeddict.html#supported-and-unsupported-operations">Operations</a> that are available to <code class="docutils literal notranslate"><span class="pre">NotRequired</span></code> items should also be available to the extra items:</p> <div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">class</span><span class="w"> </span><span class="nc">Movie</span><span class="p">(</span><span class="n">TypedDict</span><span class="p">,</span> <span class="n">extra_items</span><span class="o">=</span><span class="nb">int</span><span class="p">):</span> <span class="n">name</span><span class="p">:</span> <span class="nb">str</span> <span class="k">def</span><span class="w"> </span><span class="nf">f</span><span class="p">(</span><span class="n">movie</span><span class="p">:</span> <span class="n">Movie</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kc">None</span><span class="p">:</span> <span class="k">del</span> <span class="n">movie</span><span class="p">[</span><span class="s2">&quot;name&quot;</span><span class="p">]</span> <span class="c1"># Not OK. The value type of &#39;name&#39; is &#39;Required[int]&#39;</span> <span class="k">del</span> <span class="n">movie</span><span class="p">[</span><span class="s2">&quot;year&quot;</span><span class="p">]</span> <span class="c1"># OK. The value type of &#39;year&#39; is &#39;NotRequired[int]&#39;</span> </pre></div> </div> </section> <section id="interaction-with-unpack"> <h3><a class="toc-backref" href="#interaction-with-unpack" role="doc-backlink">Interaction with <code class="docutils literal notranslate"><span class="pre">Unpack</span></code></a></h3> <p>For type checking purposes, <code class="docutils literal notranslate"><span class="pre">Unpack[SomeTypedDict]</span></code> with extra items should be treated as its equivalent in regular parameters, and the existing rules for function parameters still apply:</p> <div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">class</span><span class="w"> </span><span class="nc">Movie</span><span class="p">(</span><span class="n">TypedDict</span><span class="p">,</span> <span class="n">extra_items</span><span class="o">=</span><span class="nb">int</span><span class="p">):</span> <span class="n">name</span><span class="p">:</span> <span class="nb">str</span> <span class="k">def</span><span class="w"> </span><span class="nf">f</span><span class="p">(</span><span class="o">**</span><span class="n">kwargs</span><span class="p">:</span> <span class="n">Unpack</span><span class="p">[</span><span class="n">Movie</span><span class="p">])</span> <span class="o">-&gt;</span> <span class="kc">None</span><span class="p">:</span> <span class="o">...</span> <span class="c1"># Should be equivalent to:</span> <span class="k">def</span><span class="w"> </span><span class="nf">f</span><span class="p">(</span><span class="o">*</span><span class="p">,</span> <span class="n">name</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">:</span> <span class="nb">int</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kc">None</span><span class="p">:</span> <span class="o">...</span> </pre></div> </div> </section> <section id="interaction-with-read-only-items"> <h3><a class="toc-backref" href="#interaction-with-read-only-items" role="doc-backlink">Interaction with Read-only Items</a></h3> <p>When the <code class="docutils literal notranslate"><span class="pre">extra_items</span></code> argument is annotated with the <code class="docutils literal notranslate"><span class="pre">ReadOnly[]</span></code> <a class="reference external" href="https://typing.python.org/en/latest/spec/glossary.html#term-type-qualifier" title="(in typing)"><span>type qualifier</span></a>, the extra items on the TypedDict have the properties of read-only items. This interacts with inheritance rules specified in <a class="reference external" href="https://typing.python.org/en/latest/spec/typeddict.html#readonly" title="(in typing)"><span class="xref std std-ref">Read-only Items</span></a>.</p> <p>Notably, if the TypedDict type specifies <code class="docutils literal notranslate"><span class="pre">extra_items</span></code> to be read-only, subclasses of the TypedDict type may redeclare <code class="docutils literal notranslate"><span class="pre">extra_items</span></code>.</p> <p>Because a non-closed TypedDict type implicitly allows non-required extra items of value type <code class="docutils literal notranslate"><span class="pre">ReadOnly[object]</span></code>, its subclass can override the <code class="docutils literal notranslate"><span class="pre">extra_items</span></code> argument with more specific types.</p> <p>More details are discussed in the later sections.</p> </section> <section id="inheritance"> <h3><a class="toc-backref" href="#inheritance" role="doc-backlink">Inheritance</a></h3> <p><code class="docutils literal notranslate"><span class="pre">extra_items</span></code> is inherited in a similar way as a regular <code class="docutils literal notranslate"><span class="pre">key:</span> <span class="pre">value_type</span></code> item. As with the other keys, the <a class="reference external" href="https://typing.python.org/en/latest/spec/typeddict.html#inheritance">inheritance rules</a> and <a class="reference external" href="https://typing.python.org/en/latest/spec/typeddict.html#readonly" title="(in typing)"><span class="xref std std-ref">Read-only Items</span></a> inheritance rules apply.</p> <p>We need to reinterpret these rules to define how <code class="docutils literal notranslate"><span class="pre">extra_items</span></code> interacts with them.</p> <blockquote> <div><ul class="simple"> <li>Changing a field type of a parent TypedDict class in a subclass is not allowed.</li> </ul> </div></blockquote> <p>First, it is not allowed to change the value of <code class="docutils literal notranslate"><span class="pre">extra_items</span></code> in a subclass unless it is declared to be <code class="docutils literal notranslate"><span class="pre">ReadOnly</span></code> in the superclass:</p> <div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">class</span><span class="w"> </span><span class="nc">Parent</span><span class="p">(</span><span class="n">TypedDict</span><span class="p">,</span> <span class="n">extra_items</span><span class="o">=</span><span class="nb">int</span> <span class="o">|</span> <span class="kc">None</span><span class="p">):</span> <span class="k">pass</span> <span class="k">class</span><span class="w"> </span><span class="nc">Child</span><span class="p">(</span><span class="n">Parent</span><span class="p">,</span> <span class="n">extra_items</span><span class="o">=</span><span class="nb">int</span><span class="p">):</span> <span class="c1"># Not OK. Like any other TypedDict item, extra_items&#39;s type cannot be changed</span> </pre></div> </div> <p>Second, <code class="docutils literal notranslate"><span class="pre">extra_items=T</span></code> effectively defines the value type of any unnamed items accepted to the TypedDict and marks them as non-required. Thus, the above restriction applies to any additional items defined in a subclass. For each item added in a subclass, all of the following conditions should apply:</p> <ul class="simple" id="pep728-inheritance-read-only"> <li>If <code class="docutils literal notranslate"><span class="pre">extra_items</span></code> is read-only<ul> <li>The item can be either required or non-required</li> <li>The item’s value type is <a class="reference external" href="https://typing.python.org/en/latest/spec/glossary.html#term-assignable" title="(in typing)"><span>assignable</span></a> to <code class="docutils literal notranslate"><span class="pre">T</span></code></li> </ul> </li> <li>If <code class="docutils literal notranslate"><span class="pre">extra_items</span></code> is not read-only<ul> <li>The item is non-required</li> <li>The item’s value type is <a class="reference external" href="https://typing.python.org/en/latest/spec/glossary.html#term-consistent" title="(in typing)"><span>consistent</span></a> with <code class="docutils literal notranslate"><span class="pre">T</span></code></li> </ul> </li> <li>If <code class="docutils literal notranslate"><span class="pre">extra_items</span></code> is not overridden, the subclass inherits it as-is.</li> </ul> <p>For example:</p> <div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">class</span><span class="w"> </span><span class="nc">MovieBase</span><span class="p">(</span><span class="n">TypedDict</span><span class="p">,</span> <span class="n">extra_items</span><span class="o">=</span><span class="nb">int</span> <span class="o">|</span> <span class="kc">None</span><span class="p">):</span> <span class="n">name</span><span class="p">:</span> <span class="nb">str</span> <span class="k">class</span><span class="w"> </span><span class="nc">MovieRequiredYear</span><span class="p">(</span><span class="n">MovieBase</span><span class="p">):</span> <span class="c1"># Not OK. Required key &#39;year&#39; is not known to &#39;MovieBase&#39;</span> <span class="n">year</span><span class="p">:</span> <span class="nb">int</span> <span class="o">|</span> <span class="kc">None</span> <span class="k">class</span><span class="w"> </span><span class="nc">MovieNotRequiredYear</span><span class="p">(</span><span class="n">MovieBase</span><span class="p">):</span> <span class="c1"># Not OK. &#39;int | None&#39; is not consistent with &#39;int&#39;</span> <span class="n">year</span><span class="p">:</span> <span class="n">NotRequired</span><span class="p">[</span><span class="nb">int</span><span class="p">]</span> <span class="k">class</span><span class="w"> </span><span class="nc">MovieWithYear</span><span class="p">(</span><span class="n">MovieBase</span><span class="p">):</span> <span class="c1"># OK</span> <span class="n">year</span><span class="p">:</span> <span class="n">NotRequired</span><span class="p">[</span><span class="nb">int</span> <span class="o">|</span> <span class="kc">None</span><span class="p">]</span> <span class="k">class</span><span class="w"> </span><span class="nc">BookBase</span><span class="p">(</span><span class="n">TypedDict</span><span class="p">,</span> <span class="n">extra_items</span><span class="o">=</span><span class="n">ReadOnly</span><span class="p">[</span><span class="nb">int</span> <span class="o">|</span> <span class="nb">str</span><span class="p">]):</span> <span class="n">title</span><span class="p">:</span> <span class="nb">str</span> <span class="k">class</span><span class="w"> </span><span class="nc">Book</span><span class="p">(</span><span class="n">BookBase</span><span class="p">,</span> <span class="n">extra_items</span><span class="o">=</span><span class="nb">str</span><span class="p">):</span> <span class="c1"># OK</span> <span class="n">year</span><span class="p">:</span> <span class="nb">int</span> <span class="c1"># OK</span> </pre></div> </div> <p>An important side effect of the inheritance rules is that we can define a TypedDict type that disallows additional items:</p> <div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">class</span><span class="w"> </span><span class="nc">MovieClosed</span><span class="p">(</span><span class="n">TypedDict</span><span class="p">,</span> <span class="n">extra_items</span><span class="o">=</span><span class="n">Never</span><span class="p">):</span> <span class="n">name</span><span class="p">:</span> <span class="nb">str</span> </pre></div> </div> <p>Here, passing the value <code class="xref py py-class docutils literal notranslate"><span class="pre">Never</span></code> to <code class="docutils literal notranslate"><span class="pre">extra_items</span></code> specifies that there can be no other keys in <code class="docutils literal notranslate"><span class="pre">MovieFinal</span></code> other than the known ones. Because of its potential common use, there is a preferred alternative:</p> <div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">class</span><span class="w"> </span><span class="nc">MovieClosed</span><span class="p">(</span><span class="n">TypedDict</span><span class="p">,</span> <span class="n">closed</span><span class="o">=</span><span class="kc">True</span><span class="p">):</span> <span class="n">name</span><span class="p">:</span> <span class="nb">str</span> </pre></div> </div> <p>where we implicitly assume that <code class="docutils literal notranslate"><span class="pre">extra_items=Never</span></code>.</p> </section> <section id="assignability"> <h3><a class="toc-backref" href="#assignability" role="doc-backlink">Assignability</a></h3> <p>Let <code class="docutils literal notranslate"><span class="pre">S</span></code> be the set of keys of the explicitly defined items on a TypedDict type. If it specifies <code class="docutils literal notranslate"><span class="pre">extra_items=T</span></code>, the TypedDict type is considered to have an infinite set of items that all satisfy the following conditions.</p> <ul class="simple"> <li>If <code class="docutils literal notranslate"><span class="pre">extra_items</span></code> is read-only:<ul> <li>The key’s value type is <a class="reference external" href="https://typing.python.org/en/latest/spec/glossary.html#term-assignable" title="(in typing)"><span>assignable</span></a> to <code class="docutils literal notranslate"><span class="pre">T</span></code>.</li> <li>The key is not in <code class="docutils literal notranslate"><span class="pre">S</span></code>.</li> </ul> </li> <li>If <code class="docutils literal notranslate"><span class="pre">extra_items</span></code> is not read-only:<ul> <li>The key is non-required.</li> <li>The key’s value type is <a class="reference external" href="https://typing.python.org/en/latest/spec/glossary.html#term-consistent" title="(in typing)"><span>consistent</span></a> with <code class="docutils literal notranslate"><span class="pre">T</span></code>.</li> <li>The key is not in <code class="docutils literal notranslate"><span class="pre">S</span></code>.</li> </ul> </li> </ul> <p>For type checking purposes, let <code class="docutils literal notranslate"><span class="pre">extra_items</span></code> be a non-required pseudo-item when checking for assignability according to rules defined in the <a class="reference external" href="https://typing.python.org/en/latest/spec/typeddict.html#readonly" title="(in typing)"><span class="xref std std-ref">Read-only Items</span></a> section, with a new rule added in bold text as follows:</p> <blockquote> <div>A TypedDict type <code class="docutils literal notranslate"><span class="pre">B</span></code> is <a class="reference external" href="https://typing.python.org/en/latest/spec/glossary.html#term-assignable" title="(in typing)"><span>assignable</span></a> to a TypedDict type <code class="docutils literal notranslate"><span class="pre">A</span></code> if <code class="docutils literal notranslate"><span class="pre">B</span></code> is <a class="reference external" href="https://typing.python.org/en/latest/spec/glossary.html#term-structural" title="(in typing)"><span class="xref std std-term">structurally</span></a> assignable to <code class="docutils literal notranslate"><span class="pre">A</span></code>. This is true if and only if all of the following are satisfied:<ul class="simple"> <li><strong>[If no key with the same name can be found in ``B``, the ‘extra_items’ argument is considered the value type of the corresponding key.]</strong></li> <li>For each item in <code class="docutils literal notranslate"><span class="pre">A</span></code>, <code class="docutils literal notranslate"><span class="pre">B</span></code> has the corresponding key, unless the item in <code class="docutils literal notranslate"><span class="pre">A</span></code> is read-only, not required, and of top value type (<code class="docutils literal notranslate"><span class="pre">ReadOnly[NotRequired[object]]</span></code>).</li> <li>For each item in <code class="docutils literal notranslate"><span class="pre">A</span></code>, if <code class="docutils literal notranslate"><span class="pre">B</span></code> has the corresponding key, the corresponding value type in <code class="docutils literal notranslate"><span class="pre">B</span></code> is assignable to the value type in <code class="docutils literal notranslate"><span class="pre">A</span></code>.</li> <li>For each non-read-only item in <code class="docutils literal notranslate"><span class="pre">A</span></code>, its value type is assignable to the corresponding value type in <code class="docutils literal notranslate"><span class="pre">B</span></code>, and the corresponding key is not read-only in <code class="docutils literal notranslate"><span class="pre">B</span></code>.</li> <li>For each required key in <code class="docutils literal notranslate"><span class="pre">A</span></code>, the corresponding key is required in <code class="docutils literal notranslate"><span class="pre">B</span></code>.</li> <li>For each non-required key in <code class="docutils literal notranslate"><span class="pre">A</span></code>, if the item is not read-only in <code class="docutils literal notranslate"><span class="pre">A</span></code>, the corresponding key is not required in <code class="docutils literal notranslate"><span class="pre">B</span></code>.</li> </ul> </div></blockquote> <p>The following examples illustrate these checks in action.</p> <p><code class="docutils literal notranslate"><span class="pre">extra_items</span></code> puts various restrictions on additional items for assignability checks:</p> <div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">class</span><span class="w"> </span><span class="nc">Movie</span><span class="p">(</span><span class="n">TypedDict</span><span class="p">,</span> <span class="n">extra_items</span><span class="o">=</span><span class="nb">int</span> <span class="o">|</span> <span class="kc">None</span><span class="p">):</span> <span class="n">name</span><span class="p">:</span> <span class="nb">str</span> <span class="k">class</span><span class="w"> </span><span class="nc">MovieDetails</span><span class="p">(</span><span class="n">TypedDict</span><span class="p">,</span> <span class="n">extra_items</span><span class="o">=</span><span class="nb">int</span> <span class="o">|</span> <span class="kc">None</span><span class="p">):</span> <span class="n">name</span><span class="p">:</span> <span class="nb">str</span> <span class="n">year</span><span class="p">:</span> <span class="n">NotRequired</span><span class="p">[</span><span class="nb">int</span><span class="p">]</span> <span class="n">details</span><span class="p">:</span> <span class="n">MovieDetails</span> <span class="o">=</span> <span class="p">{</span><span class="s2">&quot;name&quot;</span><span class="p">:</span> <span class="s2">&quot;Kill Bill Vol. 1&quot;</span><span class="p">,</span> <span class="s2">&quot;year&quot;</span><span class="p">:</span> <span class="mi">2003</span><span class="p">}</span> <span class="n">movie</span><span class="p">:</span> <span class="n">Movie</span> <span class="o">=</span> <span class="n">details</span> <span class="c1"># Not OK. While &#39;int&#39; is assignable to &#39;int | None&#39;,</span> <span class="c1"># &#39;int | None&#39; is not assignable to &#39;int&#39;</span> <span class="k">class</span><span class="w"> </span><span class="nc">MovieWithYear</span><span class="p">(</span><span class="n">TypedDict</span><span class="p">,</span> <span class="n">extra_items</span><span class="o">=</span><span class="nb">int</span> <span class="o">|</span> <span class="kc">None</span><span class="p">):</span> <span class="n">name</span><span class="p">:</span> <span class="nb">str</span> <span class="n">year</span><span class="p">:</span> <span class="nb">int</span> <span class="o">|</span> <span class="kc">None</span> <span class="n">details</span><span class="p">:</span> <span class="n">MovieWithYear</span> <span class="o">=</span> <span class="p">{</span><span class="s2">&quot;name&quot;</span><span class="p">:</span> <span class="s2">&quot;Kill Bill Vol. 1&quot;</span><span class="p">,</span> <span class="s2">&quot;year&quot;</span><span class="p">:</span> <span class="mi">2003</span><span class="p">}</span> <span class="n">movie</span><span class="p">:</span> <span class="n">Movie</span> <span class="o">=</span> <span class="n">details</span> <span class="c1"># Not OK. &#39;year&#39; is not required in &#39;Movie&#39;,</span> <span class="c1"># so it shouldn&#39;t be required in &#39;MovieWithYear&#39; either</span> </pre></div> </div> <p>Because <code class="docutils literal notranslate"><span class="pre">'year'</span></code> is absent in <code class="docutils literal notranslate"><span class="pre">Movie</span></code>, <code class="docutils literal notranslate"><span class="pre">extra_items</span></code> is considered the corresponding key. <code class="docutils literal notranslate"><span class="pre">'year'</span></code> being required violates this rule:</p> <blockquote> <div><ul class="simple"> <li>For each required key in <code class="docutils literal notranslate"><span class="pre">A</span></code>, the corresponding key is required in <code class="docutils literal notranslate"><span class="pre">B</span></code>.</li> </ul> </div></blockquote> <p>When <code class="docutils literal notranslate"><span class="pre">extra_items</span></code> is specified to be read-only on a TypedDict type, it is possible for an item to have a <a class="reference external" href="https://typing.python.org/en/latest/spec/glossary.html#term-narrow" title="(in typing)"><span class="xref std std-term">narrower</span></a> type than the <code class="docutils literal notranslate"><span class="pre">extra_items</span></code> argument:</p> <div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">class</span><span class="w"> </span><span class="nc">Movie</span><span class="p">(</span><span class="n">TypedDict</span><span class="p">,</span> <span class="n">extra_items</span><span class="o">=</span><span class="n">ReadOnly</span><span class="p">[</span><span class="nb">str</span> <span class="o">|</span> <span class="nb">int</span><span class="p">]):</span> <span class="n">name</span><span class="p">:</span> <span class="nb">str</span> <span class="k">class</span><span class="w"> </span><span class="nc">MovieDetails</span><span class="p">(</span><span class="n">TypedDict</span><span class="p">,</span> <span class="n">extra_items</span><span class="o">=</span><span class="nb">int</span><span class="p">):</span> <span class="n">name</span><span class="p">:</span> <span class="nb">str</span> <span class="n">year</span><span class="p">:</span> <span class="n">NotRequired</span><span class="p">[</span><span class="nb">int</span><span class="p">]</span> <span class="n">details</span><span class="p">:</span> <span class="n">MovieDetails</span> <span class="o">=</span> <span class="p">{</span><span class="s2">&quot;name&quot;</span><span class="p">:</span> <span class="s2">&quot;Kill Bill Vol. 2&quot;</span><span class="p">,</span> <span class="s2">&quot;year&quot;</span><span class="p">:</span> <span class="mi">2004</span><span class="p">}</span> <span class="n">movie</span><span class="p">:</span> <span class="n">Movie</span> <span class="o">=</span> <span class="n">details</span> <span class="c1"># OK. &#39;int&#39; is assignable to &#39;str | int&#39;.</span> </pre></div> </div> <p>This behaves the same way as if <code class="docutils literal notranslate"><span class="pre">year:</span> <span class="pre">ReadOnly[str</span> <span class="pre">|</span> <span class="pre">int]</span></code> is an item explicitly defined in <code class="docutils literal notranslate"><span class="pre">Movie</span></code>.</p> <p><code class="docutils literal notranslate"><span class="pre">extra_items</span></code> as a pseudo-item follows the same rules that other items have, so when both TypedDicts types specify <code class="docutils literal notranslate"><span class="pre">extra_items</span></code>, this check is naturally enforced:</p> <div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">class</span><span class="w"> </span><span class="nc">MovieExtraInt</span><span class="p">(</span><span class="n">TypedDict</span><span class="p">,</span> <span class="n">extra_items</span><span class="o">=</span><span class="nb">int</span><span class="p">):</span> <span class="n">name</span><span class="p">:</span> <span class="nb">str</span> <span class="k">class</span><span class="w"> </span><span class="nc">MovieExtraStr</span><span class="p">(</span><span class="n">TypedDict</span><span class="p">,</span> <span class="n">extra_items</span><span class="o">=</span><span class="nb">str</span><span class="p">):</span> <span class="n">name</span><span class="p">:</span> <span class="nb">str</span> <span class="n">extra_int</span><span class="p">:</span> <span class="n">MovieExtraInt</span> <span class="o">=</span> <span class="p">{</span><span class="s2">&quot;name&quot;</span><span class="p">:</span> <span class="s2">&quot;No Country for Old Men&quot;</span><span class="p">,</span> <span class="s2">&quot;year&quot;</span><span class="p">:</span> <span class="mi">2007</span><span class="p">}</span> <span class="n">extra_str</span><span class="p">:</span> <span class="n">MovieExtraStr</span> <span class="o">=</span> <span class="p">{</span><span class="s2">&quot;name&quot;</span><span class="p">:</span> <span class="s2">&quot;No Country for Old Men&quot;</span><span class="p">,</span> <span class="s2">&quot;description&quot;</span><span class="p">:</span> <span class="s2">&quot;&quot;</span><span class="p">}</span> <span class="n">extra_int</span> <span class="o">=</span> <span class="n">extra_str</span> <span class="c1"># Not OK. &#39;str&#39; is not assignable to extra items type &#39;int&#39;</span> <span class="n">extra_str</span> <span class="o">=</span> <span class="n">extra_int</span> <span class="c1"># Not OK. &#39;int&#39; is not assignable to extra items type &#39;str&#39;</span> </pre></div> </div> <p>A non-closed TypedDict type implicitly allows non-required extra keys of value type <code class="docutils literal notranslate"><span class="pre">ReadOnly[object]</span></code>. Applying the assignability rules between this type and a closed TypedDict type is allowed:</p> <div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">class</span><span class="w"> </span><span class="nc">MovieNotClosed</span><span class="p">(</span><span class="n">TypedDict</span><span class="p">):</span> <span class="n">name</span><span class="p">:</span> <span class="nb">str</span> <span class="n">extra_int</span><span class="p">:</span> <span class="n">MovieExtraInt</span> <span class="o">=</span> <span class="p">{</span><span class="s2">&quot;name&quot;</span><span class="p">:</span> <span class="s2">&quot;No Country for Old Men&quot;</span><span class="p">,</span> <span class="s2">&quot;year&quot;</span><span class="p">:</span> <span class="mi">2007</span><span class="p">}</span> <span class="n">not_closed</span><span class="p">:</span> <span class="n">MovieNotClosed</span> <span class="o">=</span> <span class="p">{</span><span class="s2">&quot;name&quot;</span><span class="p">:</span> <span class="s2">&quot;No Country for Old Men&quot;</span><span class="p">}</span> <span class="n">extra_int</span> <span class="o">=</span> <span class="n">not_closed</span> <span class="c1"># Not OK.</span> <span class="c1"># &#39;extra_items=ReadOnly[object]&#39; implicitly on &#39;MovieNotClosed&#39;</span> <span class="c1"># is not assignable to with &#39;extra_items=int&#39;</span> <span class="n">not_closed</span> <span class="o">=</span> <span class="n">extra_int</span> <span class="c1"># OK</span> </pre></div> </div> </section> <section id="interaction-with-constructors"> <h3><a class="toc-backref" href="#interaction-with-constructors" role="doc-backlink">Interaction with Constructors</a></h3> <p>TypedDicts that allow extra items of type <code class="docutils literal notranslate"><span class="pre">T</span></code> also allow arbitrary keyword arguments of this type when constructed by calling the class object:</p> <div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">class</span><span class="w"> </span><span class="nc">NonClosedMovie</span><span class="p">(</span><span class="n">TypedDict</span><span class="p">):</span> <span class="n">name</span><span class="p">:</span> <span class="nb">str</span> <span class="n">NonClosedMovie</span><span class="p">(</span><span class="n">name</span><span class="o">=</span><span class="s2">&quot;No Country for Old Men&quot;</span><span class="p">)</span> <span class="c1"># OK</span> <span class="n">NonClosedMovie</span><span class="p">(</span><span class="n">name</span><span class="o">=</span><span class="s2">&quot;No Country for Old Men&quot;</span><span class="p">,</span> <span class="n">year</span><span class="o">=</span><span class="mi">2007</span><span class="p">)</span> <span class="c1"># Not OK. Unrecognized item</span> <span class="k">class</span><span class="w"> </span><span class="nc">ExtraMovie</span><span class="p">(</span><span class="n">TypedDict</span><span class="p">,</span> <span class="n">extra_items</span><span class="o">=</span><span class="nb">int</span><span class="p">):</span> <span class="n">name</span><span class="p">:</span> <span class="nb">str</span> <span class="n">ExtraMovie</span><span class="p">(</span><span class="n">name</span><span class="o">=</span><span class="s2">&quot;No Country for Old Men&quot;</span><span class="p">)</span> <span class="c1"># OK</span> <span class="n">ExtraMovie</span><span class="p">(</span><span class="n">name</span><span class="o">=</span><span class="s2">&quot;No Country for Old Men&quot;</span><span class="p">,</span> <span class="n">year</span><span class="o">=</span><span class="mi">2007</span><span class="p">)</span> <span class="c1"># OK</span> <span class="n">ExtraMovie</span><span class="p">(</span> <span class="n">name</span><span class="o">=</span><span class="s2">&quot;No Country for Old Men&quot;</span><span class="p">,</span> <span class="n">language</span><span class="o">=</span><span class="s2">&quot;English&quot;</span><span class="p">,</span> <span class="p">)</span> <span class="c1"># Not OK. Wrong type for extra item &#39;language&#39;</span> <span class="c1"># This implies &#39;extra_items=Never&#39;,</span> <span class="c1"># so extra keyword arguments would produce an error</span> <span class="k">class</span><span class="w"> </span><span class="nc">ClosedMovie</span><span class="p">(</span><span class="n">TypedDict</span><span class="p">,</span> <span class="n">closed</span><span class="o">=</span><span class="kc">True</span><span class="p">):</span> <span class="n">name</span><span class="p">:</span> <span class="nb">str</span> <span class="n">ClosedMovie</span><span class="p">(</span><span class="n">name</span><span class="o">=</span><span class="s2">&quot;No Country for Old Men&quot;</span><span class="p">)</span> <span class="c1"># OK</span> <span class="n">ClosedMovie</span><span class="p">(</span> <span class="n">name</span><span class="o">=</span><span class="s2">&quot;No Country for Old Men&quot;</span><span class="p">,</span> <span class="n">year</span><span class="o">=</span><span class="mi">2007</span><span class="p">,</span> <span class="p">)</span> <span class="c1"># Not OK. Extra items not allowed</span> </pre></div> </div> </section> <section id="interaction-with-mapping-kt-vt"> <h3><a class="toc-backref" href="#interaction-with-mapping-kt-vt" role="doc-backlink">Interaction with Mapping[KT, VT]</a></h3> <p>A TypedDict type is <a class="reference external" href="https://typing.python.org/en/latest/spec/glossary.html#term-assignable" title="(in typing)"><span>assignable</span></a> to a type of the form <code class="docutils literal notranslate"><span class="pre">Mapping[str,</span> <span class="pre">VT]</span></code> when all value types of the items in the TypedDict are assignable to <code class="docutils literal notranslate"><span class="pre">VT</span></code>. For the purpose of this rule, a TypedDict that does not have <code class="docutils literal notranslate"><span class="pre">extra_items=</span></code> or <code class="docutils literal notranslate"><span class="pre">closed=</span></code> set is considered to have an item with a value of type <code class="docutils literal notranslate"><span class="pre">object</span></code>. This extends the current assignability rule from the <a class="reference external" href="https://typing.python.org/en/latest/spec/typeddict.html#assignability">typing spec</a>.</p> <p>For example:</p> <div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">class</span><span class="w"> </span><span class="nc">MovieExtraStr</span><span class="p">(</span><span class="n">TypedDict</span><span class="p">,</span> <span class="n">extra_items</span><span class="o">=</span><span class="nb">str</span><span class="p">):</span> <span class="n">name</span><span class="p">:</span> <span class="nb">str</span> <span class="n">extra_str</span><span class="p">:</span> <span class="n">MovieExtraStr</span> <span class="o">=</span> <span class="p">{</span><span class="s2">&quot;name&quot;</span><span class="p">:</span> <span class="s2">&quot;Blade Runner&quot;</span><span class="p">,</span> <span class="s2">&quot;summary&quot;</span><span class="p">:</span> <span class="s2">&quot;&quot;</span><span class="p">}</span> <span class="n">str_mapping</span><span class="p">:</span> <span class="n">Mapping</span><span class="p">[</span><span class="nb">str</span><span class="p">,</span> <span class="nb">str</span><span class="p">]</span> <span class="o">=</span> <span class="n">extra_str</span> <span class="c1"># OK</span> <span class="k">class</span><span class="w"> </span><span class="nc">MovieExtraInt</span><span class="p">(</span><span class="n">TypedDict</span><span class="p">,</span> <span class="n">extra_items</span><span class="o">=</span><span class="nb">int</span><span class="p">):</span> <span class="n">name</span><span class="p">:</span> <span class="nb">str</span> <span class="n">extra_int</span><span class="p">:</span> <span class="n">MovieExtraInt</span> <span class="o">=</span> <span class="p">{</span><span class="s2">&quot;name&quot;</span><span class="p">:</span> <span class="s2">&quot;Blade Runner&quot;</span><span class="p">,</span> <span class="s2">&quot;year&quot;</span><span class="p">:</span> <span class="mi">1982</span><span class="p">}</span> <span class="n">int_mapping</span><span class="p">:</span> <span class="n">Mapping</span><span class="p">[</span><span class="nb">str</span><span class="p">,</span> <span class="nb">int</span><span class="p">]</span> <span class="o">=</span> <span class="n">extra_int</span> <span class="c1"># Not OK. &#39;int | str&#39; is not assignable with &#39;int&#39;</span> <span class="n">int_str_mapping</span><span class="p">:</span> <span class="n">Mapping</span><span class="p">[</span><span class="nb">str</span><span class="p">,</span> <span class="nb">int</span> <span class="o">|</span> <span class="nb">str</span><span class="p">]</span> <span class="o">=</span> <span class="n">extra_int</span> <span class="c1"># OK</span> </pre></div> </div> <p>Type checkers should be able to infer the precise return types of <code class="docutils literal notranslate"><span class="pre">values()</span></code> and <code class="docutils literal notranslate"><span class="pre">items()</span></code> on such TypedDict types:</p> <div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">def</span><span class="w"> </span><span class="nf">fun</span><span class="p">(</span><span class="n">movie</span><span class="p">:</span> <span class="n">MovieExtraStr</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kc">None</span><span class="p">:</span> <span class="n">reveal_type</span><span class="p">(</span><span class="n">movie</span><span class="o">.</span><span class="n">items</span><span class="p">())</span> <span class="c1"># Revealed type is &#39;dict_items[str, str]&#39;</span> <span class="n">reveal_type</span><span class="p">(</span><span class="n">movie</span><span class="o">.</span><span class="n">values</span><span class="p">())</span> <span class="c1"># Revealed type is &#39;dict_values[str, str]&#39;</span> </pre></div> </div> </section> <section id="interaction-with-dict-kt-vt"> <h3><a class="toc-backref" href="#interaction-with-dict-kt-vt" role="doc-backlink">Interaction with dict[KT, VT]</a></h3> <p>Because the presence of <code class="docutils literal notranslate"><span class="pre">extra_items</span></code> on a closed TypedDict type prohibits additional required keys in its <a class="reference external" href="https://typing.python.org/en/latest/spec/glossary.html#term-structural" title="(in typing)"><span>structural</span></a> <a class="reference external" href="https://typing.python.org/en/latest/spec/glossary.html#term-subtype" title="(in typing)"><span class="xref std std-term">typing:subtypes</span></a>, we can determine if the TypedDict type and its structural subtypes will ever have any required key during static analysis.</p> <p>The TypedDict type is <a class="reference external" href="https://typing.python.org/en/latest/spec/glossary.html#term-assignable" title="(in typing)"><span>assignable</span></a> to <code class="docutils literal notranslate"><span class="pre">dict[str,</span> <span class="pre">VT]</span></code> if all items on the TypedDict type satisfy the following conditions:</p> <ul class="simple"> <li>The value type of the item is <a class="reference external" href="https://typing.python.org/en/latest/spec/glossary.html#term-consistent" title="(in typing)"><span>consistent</span></a> with <code class="docutils literal notranslate"><span class="pre">VT</span></code>.</li> <li>The item is not read-only.</li> <li>The item is not required.</li> </ul> <p>For example:</p> <div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">class</span><span class="w"> </span><span class="nc">IntDict</span><span class="p">(</span><span class="n">TypedDict</span><span class="p">,</span> <span class="n">extra_items</span><span class="o">=</span><span class="nb">int</span><span class="p">):</span> <span class="k">pass</span> <span class="k">class</span><span class="w"> </span><span class="nc">IntDictWithNum</span><span class="p">(</span><span class="n">IntDict</span><span class="p">):</span> <span class="n">num</span><span class="p">:</span> <span class="n">NotRequired</span><span class="p">[</span><span class="nb">int</span><span class="p">]</span> <span class="k">def</span><span class="w"> </span><span class="nf">f</span><span class="p">(</span><span class="n">x</span><span class="p">:</span> <span class="n">IntDict</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kc">None</span><span class="p">:</span> <span class="n">v</span><span class="p">:</span> <span class="nb">dict</span><span class="p">[</span><span class="nb">str</span><span class="p">,</span> <span class="nb">int</span><span class="p">]</span> <span class="o">=</span> <span class="n">x</span> <span class="c1"># OK</span> <span class="n">v</span><span class="o">.</span><span class="n">clear</span><span class="p">()</span> <span class="c1"># OK</span> <span class="n">not_required_num_dict</span><span class="p">:</span> <span class="n">IntDictWithNum</span> <span class="o">=</span> <span class="p">{</span><span class="s2">&quot;num&quot;</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span> <span class="s2">&quot;bar&quot;</span><span class="p">:</span> <span class="mi">2</span><span class="p">}</span> <span class="n">regular_dict</span><span class="p">:</span> <span class="nb">dict</span><span class="p">[</span><span class="nb">str</span><span class="p">,</span> <span class="nb">int</span><span class="p">]</span> <span class="o">=</span> <span class="n">not_required_num_dict</span> <span class="c1"># OK</span> <span class="n">f</span><span class="p">(</span><span class="n">not_required_num_dict</span><span class="p">)</span> <span class="c1"># OK</span> </pre></div> </div> <p>In this case, methods that are previously unavailable on a TypedDict are allowed:</p> <div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">not_required_num</span><span class="o">.</span><span class="n">clear</span><span class="p">()</span> <span class="c1"># OK</span> <span class="n">reveal_type</span><span class="p">(</span><span class="n">not_required_num</span><span class="o">.</span><span class="n">popitem</span><span class="p">())</span> <span class="c1"># OK. Revealed type is tuple[str, int]</span> </pre></div> </div> <p>However, <code class="docutils literal notranslate"><span class="pre">dict[str,</span> <span class="pre">VT]</span></code> is not necessarily assignable to a TypedDict type, because such dict can be a subtype of dict:</p> <div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">class</span><span class="w"> </span><span class="nc">CustomDict</span><span class="p">(</span><span class="nb">dict</span><span class="p">[</span><span class="nb">str</span><span class="p">,</span> <span class="nb">int</span><span class="p">]):</span> <span class="k">pass</span> <span class="n">not_a_regular_dict</span><span class="p">:</span> <span class="n">CustomDict</span> <span class="o">=</span> <span class="p">{</span><span class="s2">&quot;num&quot;</span><span class="p">:</span> <span class="mi">1</span><span class="p">}</span> <span class="n">int_dict</span><span class="p">:</span> <span class="n">IntDict</span> <span class="o">=</span> <span class="n">not_a_regular_dict</span> <span class="c1"># Not OK</span> </pre></div> </div> </section> <section id="runtime-behavior"> <h3><a class="toc-backref" href="#runtime-behavior" role="doc-backlink">Runtime behavior</a></h3> <p>At runtime, it is an error to pass both the <code class="docutils literal notranslate"><span class="pre">closed</span></code> and <code class="docutils literal notranslate"><span class="pre">extra_items</span></code> arguments in the same TypedDict definition, whether using the class syntax or the functional syntax. For simplicity, the runtime does not check other invalid combinations involving inheritance.</p> <p>For introspection, the <code class="docutils literal notranslate"><span class="pre">closed</span></code> and <code class="docutils literal notranslate"><span class="pre">extra_items</span></code> arguments are mapped to two new attributes on the resulting TypedDict object: <code class="docutils literal notranslate"><span class="pre">__closed__</span></code> and <code class="docutils literal notranslate"><span class="pre">__extra_items__</span></code>. These attributes reflect exactly what was passed to the TypedDict constructor, without considering superclasses.</p> <p>If <code class="docutils literal notranslate"><span class="pre">closed</span></code> is not passed, the value of <code class="docutils literal notranslate"><span class="pre">__closed__</span></code> is None. If <code class="docutils literal notranslate"><span class="pre">extra_items</span></code> is not passed, the value of <code class="docutils literal notranslate"><span class="pre">__extra_items__</span></code> is the new sentinel object <code class="docutils literal notranslate"><span class="pre">typing.NoExtraItems</span></code>. (It cannot be <code class="docutils literal notranslate"><span class="pre">None</span></code>, because <code class="docutils literal notranslate"><span class="pre">extra_items=None</span></code> is a valid definition that indicates all extra items must be <code class="docutils literal notranslate"><span class="pre">None</span></code>.)</p> </section> </section> <section id="how-to-teach-this"> <h2><a class="toc-backref" href="#how-to-teach-this" role="doc-backlink">How to Teach This</a></h2> <p>The choice of the spelling <code class="docutils literal notranslate"><span class="pre">&quot;extra_items&quot;</span></code> is intended to make this feature more understandable to new users compared to shorter alternatives like <code class="docutils literal notranslate"><span class="pre">&quot;extra&quot;</span></code>.</p> <p>Details of this should be documented in both the typing spec and the <a class="reference external" href="https://docs.python.org/3/library/typing.html#module-typing" title="(in Python v3.13)"><code class="xref py py-mod docutils literal notranslate"><span class="pre">typing</span></code></a> documentation.</p> </section> <section id="backwards-compatibility"> <h2><a class="toc-backref" href="#backwards-compatibility" role="doc-backlink">Backwards Compatibility</a></h2> <p>Because <code class="docutils literal notranslate"><span class="pre">extra_items</span></code> is an opt-in feature, no existing codebase will break due to this change.</p> <p>Note that <code class="docutils literal notranslate"><span class="pre">closed</span></code> and <code class="docutils literal notranslate"><span class="pre">extra_items</span></code> as keyword arguments do not collide with other keys when using something like <code class="docutils literal notranslate"><span class="pre">TD</span> <span class="pre">=</span> <span class="pre">TypedDict(&quot;TD&quot;,</span> <span class="pre">foo=str,</span> <span class="pre">bar=int)</span></code>, because this syntax has already been removed in Python 3.13.</p> <p>Because this is a type-checking feature, it can be made available to older versions as long as the type checker supports it.</p> </section> <section id="open-issues"> <h2><a class="toc-backref" href="#open-issues" role="doc-backlink">Open Issues</a></h2> <section id="use-a-special-extra-items-key-with-the-closed-class-parameter"> <h3><a class="toc-backref" href="#use-a-special-extra-items-key-with-the-closed-class-parameter" role="doc-backlink">Use a Special <code class="docutils literal notranslate"><span class="pre">__extra_items__</span></code> Key with the <code class="docutils literal notranslate"><span class="pre">closed</span></code> Class Parameter</a></h3> <p>In an earlier revision of this proposal, we discussed an approach that would utilize <code class="docutils literal notranslate"><span class="pre">__extra_items__</span></code>’s value type to specify the type of extra items accepted, like so:</p> <div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">class</span><span class="w"> </span><span class="nc">IntDict</span><span class="p">(</span><span class="n">TypedDict</span><span class="p">,</span> <span class="n">closed</span><span class="o">=</span><span class="kc">True</span><span class="p">):</span> <span class="n">__extra_items__</span><span class="p">:</span> <span class="nb">int</span> </pre></div> </div> <p>where <code class="docutils literal notranslate"><span class="pre">closed=True</span></code> is required for <code class="docutils literal notranslate"><span class="pre">__extra_items__</span></code> to be treated specially, to avoid key collision.</p> <p>Some members of the community concern about the elegance of the syntax. Practiaclly, the key collision with a regular key can be mitigated with workarounds, but since using a reserved key is central to this proposal, there are limited ways forward to address the concerns.</p> </section> <section id="support-a-new-syntax-of-specifying-keys"> <h3><a class="toc-backref" href="#support-a-new-syntax-of-specifying-keys" role="doc-backlink">Support a New Syntax of Specifying Keys</a></h3> <p>By introducing a new syntax that allows specifying string keys, we could deprecate the functional syntax of defining TypedDict types and address the key conflict issues if we decide to reserve a special key to type extra items.</p> <p>For example:</p> <div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">class</span><span class="w"> </span><span class="nc">Foo</span><span class="p">(</span><span class="n">TypedDict</span><span class="p">):</span> <span class="n">name</span><span class="p">:</span> <span class="nb">str</span> <span class="c1"># Regular item</span> <span class="n">_</span><span class="p">:</span> <span class="nb">bool</span> <span class="c1"># Type of extra items</span> <span class="n">__items__</span> <span class="o">=</span> <span class="p">{</span> <span class="s2">&quot;_&quot;</span><span class="p">:</span> <span class="nb">int</span><span class="p">,</span> <span class="c1"># Literal &quot;_&quot; as a key</span> <span class="s2">&quot;class&quot;</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="c1"># Keyword as a key</span> <span class="s2">&quot;tricky.name?&quot;</span><span class="p">:</span> <span class="nb">float</span><span class="p">,</span> <span class="c1"># Arbitrary str key</span> <span class="p">}</span> </pre></div> </div> <p>This was proposed <a class="reference external" href="https://discuss.python.org/t/pep-728-typeddict-with-typed-extra-items/45443/115">here by Jukka</a>. The <code class="docutils literal notranslate"><span class="pre">'_'</span></code> key is chosen for not needing to invent a new name, and its similarity with the match statement.</p> <p>This will allow us to deprecate the functional syntax of defining TypedDict types altogether, but there are some disadvantages. <a class="reference external" href="https://github.com/python/peps/pull/4066#discussion_r1806986861">For example</a>:</p> <ul class="simple"> <li>It’s less apparent to a reader that <code class="docutils literal notranslate"><span class="pre">_:</span> <span class="pre">bool</span></code> makes the TypedDict special, relative to adding a class argument like <code class="docutils literal notranslate"><span class="pre">extra_items=bool</span></code>.</li> <li>It’s backwards incompatible with existing TypedDicts using the <code class="docutils literal notranslate"><span class="pre">_:</span> <span class="pre">bool</span></code> key. While such users have a way to get around the issue, it’s still a problem for them if they upgrade Python (or typing-extensions).</li> <li>The types don’t appear in an annotation context, so their evaluation will not be deferred.</li> </ul> </section> </section> <section id="rejected-ideas"> <h2><a class="toc-backref" href="#rejected-ideas" role="doc-backlink">Rejected Ideas</a></h2> <section id="allowing-extra-items-without-specifying-the-type"> <h3><a class="toc-backref" href="#allowing-extra-items-without-specifying-the-type" role="doc-backlink">Allowing Extra Items without Specifying the Type</a></h3> <p><code class="docutils literal notranslate"><span class="pre">extra=True</span></code> was originally proposed for defining a TypedDict that accepts extra items regardless of the type, like how <code class="docutils literal notranslate"><span class="pre">total=True</span></code> works:</p> <div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">class</span><span class="w"> </span><span class="nc">ExtraDict</span><span class="p">(</span><span class="n">TypedDict</span><span class="p">,</span> <span class="n">extra</span><span class="o">=</span><span class="kc">True</span><span class="p">):</span> <span class="k">pass</span> </pre></div> </div> <p>Because it did not offer a way to specify the type of the extra items, the type checkers will need to assume that the type of the extra items is <code class="docutils literal notranslate"><span class="pre">Any</span></code>, which compromises type safety. Furthermore, the current behavior of TypedDict already allows untyped extra items to be present in runtime, due to <a class="reference external" href="https://typing.python.org/en/latest/spec/glossary.html#term-structural" title="(in typing)"><span>structural</span></a> <a class="reference external" href="https://typing.python.org/en/latest/spec/glossary.html#term-assignable" title="(in typing)"><span class="xref std std-term">assignability</span></a>. <code class="docutils literal notranslate"><span class="pre">closed=True</span></code> plays a similar role in the current proposal.</p> </section> <section id="support-extra-items-with-intersection"> <h3><a class="toc-backref" href="#support-extra-items-with-intersection" role="doc-backlink">Support Extra Items with Intersection</a></h3> <p>Supporting intersections in Python’s type system requires a lot of careful consideration, and it can take a long time for the community to reach a consensus on a reasonable design.</p> <p>Ideally, extra items in TypedDict should not be blocked by work on intersections, nor does it necessarily need to be supported through intersections.</p> <p>Moreover, the intersection between <code class="docutils literal notranslate"><span class="pre">Mapping[...]</span></code> and <code class="docutils literal notranslate"><span class="pre">TypedDict</span></code> is not equivalent to a TypedDict type with the proposed <code class="docutils literal notranslate"><span class="pre">extra_items</span></code> special item, as the value type of all known items in <code class="docutils literal notranslate"><span class="pre">TypedDict</span></code> needs to satisfy the is-subtype-of relation with the value type of <code class="docutils literal notranslate"><span class="pre">Mapping[...]</span></code>.</p> </section> <section id="requiring-type-compatibility-of-the-known-items-with-extra-items"> <h3><a class="toc-backref" href="#requiring-type-compatibility-of-the-known-items-with-extra-items" role="doc-backlink">Requiring Type Compatibility of the Known Items with <code class="docutils literal notranslate"><span class="pre">extra_items</span></code></a></h3> <p><code class="docutils literal notranslate"><span class="pre">extra_items</span></code> restricts the value type for keys that are <em>unknown</em> to the TypedDict type. So the value type of any <em>known</em> item is not necessarily assignable to <code class="docutils literal notranslate"><span class="pre">extra_items</span></code>, and <code class="docutils literal notranslate"><span class="pre">extra_items</span></code> is not necessarily assignable to the value types of all known items.</p> <p>This differs from TypeScript’s <a class="reference external" href="https://www.typescriptlang.org/docs/handbook/2/objects.html#index-signatures">Index Signatures</a> syntax, which requires all properties’ types to match the string index’s type. For example:</p> <div class="highlight-typescript notranslate"><div class="highlight"><pre><span></span><span class="kd">interface</span><span class="w"> </span><span class="nx">MovieWithExtraNumber</span><span class="w"> </span><span class="p">{</span> <span class="w"> </span><span class="nx">name</span><span class="o">:</span><span class="w"> </span><span class="kt">string</span><span class="w"> </span><span class="c1">// Property &#39;name&#39; of type &#39;string&#39; is not assignable to &#39;string&#39; index type &#39;number&#39;.</span> <span class="w"> </span><span class="p">[</span><span class="nx">index</span><span class="o">:</span><span class="w"> </span><span class="kt">string</span><span class="p">]</span><span class="o">:</span><span class="w"> </span><span class="kt">number</span> <span class="p">}</span> <span class="kd">interface</span><span class="w"> </span><span class="nx">MovieWithExtraNumberOrString</span><span class="w"> </span><span class="p">{</span> <span class="w"> </span><span class="nx">name</span><span class="o">:</span><span class="w"> </span><span class="kt">string</span><span class="w"> </span><span class="c1">// OK</span> <span class="w"> </span><span class="p">[</span><span class="nx">index</span><span class="o">:</span><span class="w"> </span><span class="kt">string</span><span class="p">]</span><span class="o">:</span><span class="w"> </span><span class="kt">number</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">string</span> <span class="p">}</span> </pre></div> </div> <p>This is a known limitation discussed in <a class="reference external" href="https://github.com/microsoft/TypeScript/issues/17867">TypeScript’s issue tracker</a>, where it is suggested that there should be a way to exclude the defined keys from the index signature so that it is possible to define a type like <code class="docutils literal notranslate"><span class="pre">MovieWithExtraNumber</span></code>.</p> </section> </section> <section id="reference-implementation"> <h2><a class="toc-backref" href="#reference-implementation" role="doc-backlink">Reference Implementation</a></h2> <p>An earlier revision of proposal is supported in <a class="reference external" href="https://github.com/microsoft/pyright/releases/tag/1.1.352">pyright 1.1.352</a>, and <a class="reference external" href="https://github.com/quora/pyanalyze/releases/tag/v0.12.0">pyanalyze 0.12.0</a>.</p> </section> <section id="acknowledgments"> <h2><a class="toc-backref" href="#acknowledgments" role="doc-backlink">Acknowledgments</a></h2> <p>Thanks to Jelle Zijlstra for sponsoring this PEP and providing review feedback, Eric Traut who <a class="reference external" href="https://mail.python.org/archives/list/typing-sig&#64;python.org/message/3Z72OQWVTOVS6UYUUCCII2UZN56PV5II/">proposed the original design</a> this PEP iterates on, and Alice Purcell for offering their perspective as the author of <a class="pep reference internal" href="../pep-0705/" title="PEP 705 – TypedDict: Read-only items">PEP 705</a>.</p> </section> <section id="copyright"> <h2><a class="toc-backref" href="#copyright" role="doc-backlink">Copyright</a></h2> <p>This document is placed in the public domain or under the CC0-1.0-Universal license, whichever is more permissive.</p> </section> </section> <hr class="docutils" /> <p>Source: <a class="reference external" href="https://github.com/python/peps/blob/main/peps/pep-0728.rst">https://github.com/python/peps/blob/main/peps/pep-0728.rst</a></p> <p>Last modified: <a class="reference external" href="https://github.com/python/peps/commits/main/peps/pep-0728.rst">2025-03-05 16:28:34 GMT</a></p> </article> <nav id="pep-sidebar"> <h2>Contents</h2> <ul> <li><a class="reference internal" href="#abstract">Abstract</a></li> <li><a class="reference internal" href="#motivation">Motivation</a><ul> <li><a class="reference internal" href="#disallowing-extra-items-explicitly">Disallowing Extra Items Explicitly</a></li> <li><a class="reference internal" href="#allowing-extra-items-of-a-certain-type">Allowing Extra Items of a Certain Type</a></li> <li><a class="reference internal" href="#support-additional-keys-for-unpack">Support Additional Keys for <code class="docutils literal notranslate"><span class="pre">Unpack</span></code></a></li> </ul> </li> <li><a class="reference internal" href="#rationale">Rationale</a></li> <li><a class="reference internal" href="#specification">Specification</a><ul> <li><a class="reference internal" href="#the-extra-items-class-parameter">The <code class="docutils literal notranslate"><span class="pre">extra_items</span></code> Class Parameter</a></li> <li><a class="reference internal" href="#the-closed-class-parameter">The <code class="docutils literal notranslate"><span class="pre">closed</span></code> Class Parameter</a></li> <li><a class="reference internal" href="#interaction-with-totality">Interaction with Totality</a></li> <li><a class="reference internal" href="#interaction-with-unpack">Interaction with <code class="docutils literal notranslate"><span class="pre">Unpack</span></code></a></li> <li><a class="reference internal" href="#interaction-with-read-only-items">Interaction with Read-only Items</a></li> <li><a class="reference internal" href="#inheritance">Inheritance</a></li> <li><a class="reference internal" href="#assignability">Assignability</a></li> <li><a class="reference internal" href="#interaction-with-constructors">Interaction with Constructors</a></li> <li><a class="reference internal" href="#interaction-with-mapping-kt-vt">Interaction with Mapping[KT, VT]</a></li> <li><a class="reference internal" href="#interaction-with-dict-kt-vt">Interaction with dict[KT, VT]</a></li> <li><a class="reference internal" href="#runtime-behavior">Runtime behavior</a></li> </ul> </li> <li><a class="reference internal" href="#how-to-teach-this">How to Teach This</a></li> <li><a class="reference internal" href="#backwards-compatibility">Backwards Compatibility</a></li> <li><a class="reference internal" href="#open-issues">Open Issues</a><ul> <li><a class="reference internal" href="#use-a-special-extra-items-key-with-the-closed-class-parameter">Use a Special <code class="docutils literal notranslate"><span class="pre">__extra_items__</span></code> Key with the <code class="docutils literal notranslate"><span class="pre">closed</span></code> Class Parameter</a></li> <li><a class="reference internal" href="#support-a-new-syntax-of-specifying-keys">Support a New Syntax of Specifying Keys</a></li> </ul> </li> <li><a class="reference internal" href="#rejected-ideas">Rejected Ideas</a><ul> <li><a class="reference internal" href="#allowing-extra-items-without-specifying-the-type">Allowing Extra Items without Specifying the Type</a></li> <li><a class="reference internal" href="#support-extra-items-with-intersection">Support Extra Items with Intersection</a></li> <li><a class="reference internal" href="#requiring-type-compatibility-of-the-known-items-with-extra-items">Requiring Type Compatibility of the Known Items with <code class="docutils literal notranslate"><span class="pre">extra_items</span></code></a></li> </ul> </li> <li><a class="reference internal" href="#reference-implementation">Reference Implementation</a></li> <li><a class="reference internal" href="#acknowledgments">Acknowledgments</a></li> <li><a class="reference internal" href="#copyright">Copyright</a></li> </ul> <br> <a id="source" href="https://github.com/python/peps/blob/main/peps/pep-0728.rst">Page Source (GitHub)</a> </nav> </section> <script src="../_static/colour_scheme.js"></script> <script src="../_static/wrap_tables.js"></script> <script src="../_static/sticky_banner.js"></script> </body> </html>

Pages: 1 2 3 4 5 6 7 8 9 10