Skip to content

TypedDict with total=False and NotRequired still throws an error when instantiated without all fields #19932

@jonded94

Description

@jonded94

Bug Report

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)

All other type checkers which I know of don't throw a warning here:

$ ty check mypytest.py 
WARN ty is pre-release software and not ready for production use. Expect to encounter bugs, missing features, and fatal errors.
Checking ------------------------------------------------------------ 1/1 files                                                                                                                                                                                                                                     All checks passed!                                 
$ pyrefly check mypytest.py 
 INFO 0 errors
$ pyright mypytest.py
0 errors, 0 warnings, 0 informations

But mypy throws this:

$  mypy --strict mypytest.py
mypytest.py:10: error: Non-required key "a" not explicitly found in any ** item  [typeddict-item]
Found 1 error in 1 file (checked 1 source file)

Why is it a problem that a "Non-required" key wasn't found? What is this error message about?

For reference, setting total=True and removing the NotRequired wrapper will lead to all of these type checkers throwing an error:

$ pyright mypytest.py 
WARNING: there is a new pyright version available (v1.1.398 -> v1.1.405).
Please install the new version or set PYRIGHT_PYTHON_FORCE_VERSION to `latest`

error: No overloads for "__init__" match the provided arguments
    Argument types: (**Bar) (reportCallIssue)
1 error, 0 warnings, 0 information
$ pyrefly check mypytest.py
ERROR Missing argument `a` in function `Foo.__init__` [missing-argument]
  --> mypytest.py:10:8
   |
10 | f = Foo(**g)
   |        ^^^^^
   |
 INFO 1 error
$  ty check mypytest.py
WARN ty is pre-release software and not ready for production use. Expect to encounter bugs, missing features, and fatal errors.
Checking ------------------------------------------------------------ 1/1 files                                                                                                                                                                                                                                     error[missing-typed-dict-key]: Missing required key 'a' in TypedDict `Foo` constructor                                                                                                                                                                                                                              
  --> mypytest.py:10:5
   |
 9 | g = Bar()
10 | f = Foo(**g)
   |     ^^^
   |
info: rule `missing-typed-dict-key` is enabled by default

Found 1 diagnostic

Even mypy throws an error here, but interestingly, it's a different one:

$ mypy --strict mypytest.py
mypytest.py:10: error: Missing key "a" for TypedDict "Foo"  [typeddict-item]
Found 1 error in 1 file (checked 1 source file)

To Reproduce

mypy --strict mypytest.py

Expected Behavior

Not throw an warning/error.

Actual Behavior

mypytest.py:10: error: Non-required key "a" not explicitly found in any ** item  [typeddict-item]
Found 1 error in 1 file (checked 1 source file)

Your Environment

  • Mypy version used: 1.18.2
  • Mypy command-line flags: --strict
  • Python version used: 3.12.9

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions