Skip to content

Fix TypedDict in narrowing wrongly eliminating subtype for extra_items keys - #11610

Open
Nilesh Patil (nileshpatil6) wants to merge 2 commits into
microsoft:mainfrom
nileshpatil6:fix-typeddict-extraitems-narrowing
Open

Fix TypedDict in narrowing wrongly eliminating subtype for extra_items keys#11610
Nilesh Patil (nileshpatil6) wants to merge 2 commits into
microsoft:mainfrom
nileshpatil6:fix-typeddict-extraitems-narrowing

Conversation

@nileshpatil6

Copy link
Copy Markdown
Contributor

For a TypedDict with extra_items (PEP 728), a membership check on a key that is not a known item wrongly makes the negative branch unreachable:

class Movie(TypedDict, extra_items=bool):
    name: str

def func(movie: Movie) -> None:
    if "novel_adaptation" in movie:
        return
    reveal_type(movie)  # branch is wrongly marked unreachable today

The cause is in narrowTypeForTypedDictKey: the negative branch used knownItems.get(key) ?? extraItems, so a key that only matched the extra_items pseudo-entry was treated like a known provided item and eliminated the subtype. An extra item may or may not be present at runtime, so its absence can never rule the type out. Only a known item that is required or provided can do that.

The positive branch keeps its current behavior, it still uses the extra_items entry to mark the key as provided after the check.

Testing: added a case to typedDictClosed1.py where code after a negative in check on an extra key must stay reachable (it asserts an unrelated type error is still reported there, which the unreachability bug swallows). Without the fix that sample reports 7 errors instead of 8, with it all 10 TypedDictClosed tests pass and the full typeEvaluator suite is green: 1184 tests across 8 suites, 0 failures.

…ems keys

In narrowTypeForTypedDictKey the negative branch treated a key that only
matched the extra_items pseudo-entry the same as a known item, so for a
closed TypedDict with extra_items a check like 'if "key" in td' made the
else branch unreachable. An extra item may or may not be present, so only
a known required or provided item can eliminate the subtype.
@StellaHuang95

Stella Huang (StellaHuang95) commented Aug 10, 2026

Copy link
Copy Markdown
Collaborator

🔒 Automated review in progress — Stella Huang (@StellaHuang95) is auto-reviewing this PR.


# This should generate a type incompatibility error. If the statements
# above are incorrectly marked unreachable, no error is reported here.
movie["other3"] = 1

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Warning · Non-blocking recommendation

Please also cover a negative in check on a known required key (such as name) and assert that its following code remains unreachable. This would protect the existing narrowing behavior against an accidental over-relaxation of this change. [verified]

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved via Review Center.

@StellaHuang95 Stella Huang (StellaHuang95) added the review-auto:approved Automated review: no blocking findings (approval posted). label Aug 10, 2026
@nileshpatil6

Copy link
Copy Markdown
Contributor Author

Good call. Added in f379f89: a negative in check on name (required known item) with an assignment in the branch that would be a type error if the branch were reachable. The sample's expected error count is unchanged, so the test fails if that branch ever becomes reachable.

Also sanity checked it guards what you had in mind: if I over-relax the fix to return the subtype unconditionally in the negative branch, TypedDictClosed1 fails on exactly this case; with the fix as written, all 10 TypedDictClosed tests pass.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved via Review Center.

@rchiodo Rich Chiodo (rchiodo) left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved via Review Center.

@rchiodo Rich Chiodo (rchiodo) left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved via Review Center.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

review-auto:approved Automated review: no blocking findings (approval posted).

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants