Since Python 3.11, as per PEP 655, what you need is NotRequired:

class _trending(TypedDict):
    allStores: NotRequired[bool]
    category: str
    date: str
    average: List[int]

notice that you shouldn't use Optional in TypedDict, and only use (Not)Required

if you want to use TypedDict with Pydantic, you could refer this article

Answer from S_SmJaE on Stack Overflow
🌐
Python
peps.python.org › pep-0655
PEP 655 – Marking individual TypedDict items as required or potentially-missing | peps.python.org
This PEP introduces two new notations: Required[], which can be used on individual items of a TypedDict to mark them as required, and NotRequired[], which can be used on individual items to mark them as potentially-missing.
🌐
Python
typing.python.org › en › latest › spec › typeddict.html
Typed dictionaries — typing documentation
If the NotRequired qualifier is present, the item is non-required. If the total argument of the TypedDict definition is False, the item is non-required.
🌐
Python.org
discuss.python.org › peps
PEP 655: Required[] and NotRequired[] for TypedDict - PEPs - Discussions on Python.org
February 20, 2022 - This PEP enhances TypedDict - an existing kind of type annotation for describing dictionaries with a known set of named keys - with the ability to directly mark individual keys as Required[] or as NotRequired[], rather than requiring the indirect ...
Top answer
1 of 2
117

Since Python 3.11, as per PEP 655, what you need is NotRequired:

class _trending(TypedDict):
    allStores: NotRequired[bool]
    category: str
    date: str
    average: List[int]

notice that you shouldn't use Optional in TypedDict, and only use (Not)Required

if you want to use TypedDict with Pydantic, you could refer this article

2 of 2
18

I used this question as duplicate target, but noticed that another option is missing here.

If you don't like NotRequired (for example, if you have many required and many optional keys and don't want to repeat NotRequired many times) or don't want to bother with typing_extensions (rare case), you can tweak totality.

The following definitions of Main* are equivalent:

import sys
# You may also pick one without version check, of course
if sys.version_info < (3, 11):
    from typing_extensions import TypedDict, Required, NotRequired
else:
    from typing import TypedDict, Required, NotRequired


class Main1(TypedDict):
    foo: int
    bar: str
    baz: NotRequired[int]
    qux: NotRequired[str]


class Main2(TypedDict, total=False):
    foo: Required[int]
    bar: Required[str]
    baz: int
    qux: str


class _Main3(TypedDict):
    foo: int
    bar: str

class Main3(_Main3, total=False):
    baz: int
    qux: str


class _Main4(TypedDict, total=False):
    baz: int
    qux: str
    
class Main4(_Main4):
    foo: int
    bar: str

Here's PEP explanation of totality:

The totality flag only applies to items defined in the body of the TypedDict definition. Inherited items won’t be affected, and instead use totality of the TypedDict type where they were defined. This makes it possible to have a combination of required and non-required keys in a single TypedDict type.

Here's example of checking with definitions above:

Main1(foo=1, bar='bar', baz=2, qux='qux')
Main1(foo=1, bar='bar', baz=2)
Main1(foo=1, bar='bar')
Main1(foo=1, baz=2, qux='qux')  # E: Missing key "bar" for TypedDict "Main1"  [typeddict-item]
Main1(foo=1, bar='bar', who=None)  # E: Extra key "who" for TypedDict "Main1"  [typeddict-item]

Main2(foo=1, bar='bar', baz=2, qux='qux')
Main2(foo=1, bar='bar', baz=2)
Main2(foo=1, bar='bar')
Main2(foo=1, baz=2, qux='qux')  # E: Missing key "bar" for TypedDict "Main2"  [typeddict-item]
Main2(foo=1, bar='bar', who=None)  # E: Extra key "who" for TypedDict "Main2"  [typeddict-item]

Main3(foo=1, bar='bar', baz=2, qux='qux')
Main3(foo=1, bar='bar', baz=2)
Main3(foo=1, bar='bar')
Main3(foo=1, baz=2, qux='qux')  # E: Missing key "bar" for TypedDict "Main3"  [typeddict-item]
Main3(foo=1, bar='bar', who=None)  # E: Extra key "who" for TypedDict "Main3"  [typeddict-item]

Main4(foo=1, bar='bar', baz=2, qux='qux')
Main4(foo=1, bar='bar', baz=2)
Main4(foo=1, bar='bar')
Main4(foo=1, baz=2, qux='qux')  # E: Missing key "bar" for TypedDict "Main4"  [typeddict-item]
Main4(foo=1, bar='bar', who=None)  # E: Extra key "who" for TypedDict "Main4"  [typeddict-item]

You can fiddle with this in playground

🌐
Mypy
mypy.readthedocs.io › en › stable › typed_dict.html
TypedDict - mypy 1.19.1 documentation
A TypedDict with a mix of required and non-required keys, such as Movie above, will only be compatible with another TypedDict if all required keys in the other TypedDict are required keys in the first TypedDict, and all non-required keys of the other TypedDict are also non-required keys in the first TypedDict. You can use typing.ReadOnly, introduced in Python 3.13, or typing_extensions.ReadOnly to mark TypedDict items as read-only (PEP 705):
🌐
GitHub
github.com › python › typing › issues › 1454
Allow use of `Required` and `NotRequired` to make an existing typed dict total or optional · Issue #1454 · python/typing
August 28, 2023 - Required/NotRequired would include all keys of the typed dict and all its superclasses. This differs from how total works. Example: class TD1(TypedDict): a: int class TD2(TypedDict, total=False): b: int TDR = Required[TD2] # b becomes required ...
Published   Aug 28, 2023
🌐
GitHub
github.com › python › typing › discussions › 1516
Required/NotRequired and inheritance · python/typing · Discussion #1516
In my previous post, I forgot about TypedDict definitions that extend multiple other TypedDict definitions. If we go with option 2 or 3, it should also apply to the multiple inheritance case. from typing import NotRequired, Required, TypedDict class A1(TypedDict): a: NotRequired[str] class A2(TypedDict): a: Required[str] class B(A1, A2): # Should generate an error ...
Author   python
Find elsewhere
🌐
Python.org
discuss.python.org › typing
Extending a `TypedDict` with non-required fields - Typing - Discussions on Python.org
November 23, 2024 - Hello. There is something in the assignment rules involving TypedDicts inheritance that I don’t understand, or that mypy gets wrong. Assume the following TypedDict declarations: class A(typing.TypedDict, total=False): a: int class B(typing.TypedDict, total=False): a: int b: int As expected, this is valid code: a: A = {'a': 1} b: B = {'a': 1} However, unexpectedly for me, this is not: a: A = {'a': 1} b: B = a At the same time: b: B = {'a': 1, 'b': 2} a: A = b is considered va...
🌐
GitHub
github.com › robotframework › robotframework › issues › 5115
`NotRequired` and `Required` not properly handled with Python < 3.11 in `TypedDict` conversion · Issue #5115 · robotframework/robotframework
April 17, 2024 - Hi All, I have a few classes that use the NotRequired notation from typing_extensions_4.8.0 and python 3.9.13 class Signal(TypedDict): Network: str Signal: str class WriteSignal(Signal): Value: str Interval: NotRequired[int] This worked ...
Published   Apr 17, 2024
🌐
JetBrains
youtrack.jetbrains.com › issue › PY-53611 › Support-PEP-655-typing.Required-typing.NotRequired-for-TypedDicts
Support PEP 655 typing.Required/typing.NotRequired for ...
{{ (>_<) }} This version of your browser is not supported. Try upgrading to the latest stable version. Something went seriously wrong
🌐
GitHub
github.com › python › mypy › issues › 19932
`TypedDict` with `total=False` and `NotRequired` still throws an error when instantiated without all fields · Issue #19932 · python/mypy
September 26, 2025 - In this example, all fields (b and a) are not required, the field a (which is the problem here) is not required by both wrapping it in NotRequired and having its class be annotated with total=False: from typing import TypedDict, NotRequired class Bar(TypedDict, total=False): b: str class Foo(Bar, TypedDict, total=False): a: NotRequired[int] g = Bar() f = Foo(**g)
Published   Sep 26, 2025
🌐
Python
peps.python.org › pep-0705
PEP 705 – TypedDict: Read-only items | peps.python.org
All non-specified items in a TypedDict implicitly have value type ReadOnly[NotRequired[object]].
🌐
Python documentation
docs.python.org › 3 › library › typing.html
typing — Support for type hints
See TypedDict and PEP 655 for more details. Added in version 3.11. typing.NotRequired¶ · Special typing construct to mark a TypedDict key as potentially missing. See TypedDict and PEP 655 for more details.
🌐
JetBrains
youtrack.jetbrains.com › issue › PY-58129 › False-Positives-with-NotRequired-in-Generic-TypedDict
False Positives with NotRequired in Generic TypedDict
{{ (>_<) }} This version of your browser is not supported. Try upgrading to the latest stable version. Something went seriously wrong
🌐
Python
peps.python.org › pep-0692
PEP 692 – Using TypedDict for more precise **kwargs typing | peps.python.org
This behaviour can be overridden by setting the dictionary’s total parameter as False. Moreover, PEP 655 introduced new type qualifiers - typing.Required and typing.NotRequired - that enable specifying whether a particular key is required or not:
🌐
GitHub
github.com › python › mypy › issues › 17329
Accessing a `NotRequired` field in a `TypedDict` does not error · Issue #17329 · python/mypy
June 4, 2024 - Bug Report Accessing a NotRequired field in a TypedDict does not result in an error. To Reproduce from typing import NotRequired, TypedDict TypeDef = TypedDict( "TypeDef", { "Field": NotRequired[str], }, ) sample = TypeDef() val: str = s...
Published   Jun 04, 2024
🌐
Reddit
reddit.com › r/learnpython › how to type hint dictionaries without using any?
r/learnpython on Reddit: How to type hint dictionaries without using Any?
February 28, 2024 -

Hi folks I tried to keep the title as short and concise as possible, I often find myself struggling for hours in order to properly type annotate nested dictionaries in my code.

If the dict only has 1 level, it works well but with a tiny more complex ones like the following, I have a lot of trouble:

{"@context": "http://schema.org",
 "@type": "VideoObject",
 "url": "https://www.example.com/0000/adventures-music-videos.html",
 "name": "sample movie",
 "description": "example",
 "thumbnailUrl": "thumbnail.com/1.jpg",
 "uploadDate": "2023-11-29",
 "duration": "PT2H20M20S",
 "contentUrl": "sample_link",
 "isAccessibleForFree": false,
 "productionCompany": {"@type": "Organization",
  "name": "Example",
  "@id": "https://www.example.com/0000/studio/music-movies.html"},
 "hasPart": [{"@type": "Clip",
   "name": "scene 1",
   "startOffset": 15,
   "endOffset": 2126,
   "url": "https://www.example.com/0000/adventures-music-videos.html#scene_0000",
   "isAccessibleForFree": false},
  {"@type": "Clip",
   "name": "scene 2",
   "startOffset": 2128,
   "endOffset": 4114,
   "url": "https://www.example.com/0000/adventures-music-videos.html#scene_0000",
   "isAccessibleForFree": false},
  {"@type": "Clip",
   "name": "scene 3",
   "startOffset": 4117,
   "endOffset": 6055,
   "url": "https://www.example.com/0000/adventures-music-videos.html#scene_0000",
   "isAccessibleForFree": false},
  {"@type": "Clip",
   "name": "scene 4",
   "startOffset": 6057,
   "endOffset": 8414,
   "url": "https://www.example.com/0000/adventures-music-videos.html#scene_0000",
   "isAccessibleForFree": false}]}

I tried dict[str, object] and I also tried a looong annotation with all the possible types separated by a pipe, but for now the only one that does not trigger mypy in strict mode is dict[str, Any].

Any clues as to what I might be doing wrong?