"key" in typed_dict checks for non-closed TypedDict doesn't allow typed_dict["key"] afterwards
Nobody has claimed this yet.
- Dominant language
- Python
- Stars
- 15.6k
- Forks
- 1.8k
- Avg merge
- 12h 13m
- Merged PRs (30d)
- 52
Description
Note: This is very related to #10376 and the regressed (?) fix thereof. The behavior in versions other than 1.1.401 is to simply deduce `Never`, but even 1.1.401 seems less than helpful.
Consider the following ([Pyright-Play](https://pyright-play.net/?pyrightVersion=1.1.401&pythonVersion=3.15&code=GYJw9gtgBALgngBwJYDsDmUkQWEMoAqiApgCYAiSAxjADRQByYMASsQI4CuSIZAsAChBVADYBDAM4SoAQQAURBGUo0AlAC5BUbVDHrMKfAB9GYFMS06ARvqasO3XqQDaqGAF1Bg0sWBRgnChUcjD6MhqW2kh%2BMM4ARGJx7poCOmlQvABuxGIiAPrwSiHxie6qUADEUADCuLw0InBQKGIg4ADuZLBgUAAGbr1eqTpZOfmFxMVxVknlVbVtxA1NvBKcIjDSqLooUMRtuJGYftNxBrAp6SPE2bkFJFMzZZU1dUswjc2tHV0wPf2GQbDbRHUZ3CZTKizF4LeofFbENYbLa7MS7fbgEBHaJQOJQ86hI5pMHjB4wOZQADKAAswOtSFArMQ%2BjJevQUMw%2BgwbvsgVdtCT7kVYnjoVUaXSRAyOfgEOBSJwqMy0XsDiAgA) - as per the note above, Pyright is set to 1.1.401 to see any "non-trivial" type deduction)
```python
from typing import TypedDict, NotRequired
class A(TypedDict):
a: int | None
b: NotRequired[int]
def func(t: A):
if t["a"]:
reveal_type(t["a"]) # Correctly narrowed to `int`
reveal_type(t["b"]) # Correctly results in an error
if "b" in t:
reveal_type(t["b"]) # Correctly narrowed to `int`
reveal_type(t["c"]) # Correctly results in an error
if "c" in t:
reveal_type(t) # Should be `A`, not `Never`
reveal_type(t["c"]) # Should not produce an error
```
* As the top `if` shows, items within a `TypedDict` can properly be narrowed (which is very useful)
* As the middle `if` shows, optional keys can correctly be accessed once a `"key" in typed_dict` check has passed
* However, the same type of guard does not work for unknown keys, which should in principle be allowed: Ignoring the fact that the latest versions once again incorrectly infer `t` to be `Never`, it would be very helpful if `t["c"]` didn't throw an error (and instead simply resolved to `Any`). Otherwise, it's effectively impossible to ever convince Pyright that the dict has some additional key. Note that adding `extra_items = Any` correctly fixes the last line to reveal `Any` (in 1.1.407), but it also makes the line before the `if` guard pass without errors (this might be a separate bug?). It was my interpretation that in versions before `extra_items` was added, the behavior should be equivalent to `extra_items=Any`?
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by running the TypedDict example in Pyright-Play with versions 1.1.401 and 1.1.407, then trace the handling of the `"key" in typed_dict` guard and unknown-key access. Done means the guard no longer narrows the TypedDict to `Never`, and `t["c"]` resolves to `Any` after the guard while still erroring before it.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- python
- Domain
- devtools
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100