Skip to content

feat: proper narrowing for TypedDict keys and values #19610

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: master
Choose a base branch
from

Conversation

BobTheBuidler
Copy link
Contributor

This PR enhances type narrowing for TypedDict.keys and TypedDict.values.

Keys will now type as a Union of Literals and values will type as a Union of the value types.

I will do TypedDict.items in a separate PR, as I'm having trouble with the typeshed.

(Explain how this PR changes mypy.)

@@ -26,8 +26,8 @@ from typing import TypedDict
Point = TypedDict('Point', {'x': int, 'y': int})
p = Point({'x': 42, 'y': 1337})
reveal_type(p) # N: Revealed type is "TypedDict('__main__.Point', {'x': builtins.int, 'y': builtins.int})"
# Use values() to check fallback value type.
reveal_type(p.values()) # N: Revealed type is "typing.Iterable[builtins.object]"
reveal_type(p.values()) # N: Revealed type is "typing.ValuesView[builtins.int]"
Copy link
Member

Choose a reason for hiding this comment

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

This is incorrect since the TypedDict is not closed, so there may be other values that are not of this type.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Wait why isn't it closed? If you try to set p['not-present'] = 123 you will get an error from mypy

Copy link
Member

Choose a reason for hiding this comment

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

There can be subclasses. The upcoming PEP 728 addresses this.

Copy link
Contributor

github-actions bot commented Aug 7, 2025

Diff from mypy_primer, showing the effect of this PR on open source code:

scrapy (https://github.com/scrapy/scrapy)
+ scrapy/utils/request.py:230: error: If x = b'abc' then f"{x}" or "{}".format(x) produces "b'abc'", not "abc". If this is desired behavior, use f"{x!r}" or "{!r}".format(x). Otherwise, decode the bytes  [str-bytes-safe]

yarl (https://github.com/aio-libs/yarl)
+ tests/test_cache.py:18:12: error: Non-overlapping equality check (left operand type: "KeysView[Literal['idna_encode', 'idna_decode', 'ip_address', 'host_validate', 'encode_host']]", right operand type: "set[str]")  [comparison-overlap]
+ tests/test_cache.py:18:12: note: See https://mypy.rtfd.io/en/stable/_refs.html#code-comparison-overlap for more info

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants