Skip to content

[ty] Avoid unsound TypedDict dictionary disjointness#26859

Draft
charliermarsh wants to merge 2 commits into
charlie/td-narrow-emptyfrom
charlie/typed-dict-dictionary-disjointness
Draft

[ty] Avoid unsound TypedDict dictionary disjointness#26859
charliermarsh wants to merge 2 commits into
charlie/td-narrow-emptyfrom
charlie/typed-dict-dictionary-disjointness

Conversation

@charliermarsh

@charliermarsh charliermarsh commented Jul 15, 2026

Copy link
Copy Markdown
Member

Summary

This is a focused follow-up to #25851.

When comparing a TypedDict with another type, we currently approximate every TypedDict as dict[str, Any]. Because dictionary key parameters are invariant, that can incorrectly prove a TypedDict disjoint from dictionary types whose key domains admit its actual string-literal keys, such as dict[Literal["x"], object] or dict[object, object]. Narrowing can then eliminate a reachable branch, including an isinstance() check whose generic class-info can instantiate to dict.

The same issue affects identity narrowing against empty dictionaries. An optional TypedDict can be empty at runtime, so comparing it with a dict[Never, Never] must not make the identity branch unreachable.

We now use the gradual projection dict[Any, Any] for this fallback. This deliberately refuses to prove disjointness unless we have stronger evidence, while preserving the fact that a TypedDict is disjoint from proper dict subclasses.

This remains intentionally conservative. We do not derive a precise dictionary projection from required fields, so TypedDict versus Mapping[int, object] remains non-disjoint, and required field value types are not used to prove disjointness from types such as dict[str, str]. The change also does not add broader materialization or correlation-aware refinement for generic class-info types.

@astral-sh-bot

astral-sh-bot Bot commented Jul 15, 2026

Copy link
Copy Markdown

Typing conformance results

No changes detected ✅

Current numbers
The percentage of diagnostics emitted that were expected errors held steady at 94.70%. The percentage of expected errors that received a diagnostic held steady at 90.27%. The number of fully passing files held steady at 97/134.

@astral-sh-bot

astral-sh-bot Bot commented Jul 15, 2026

Copy link
Copy Markdown

Memory usage report

Memory usage unchanged ✅

@astral-sh-bot

astral-sh-bot Bot commented Jul 15, 2026

Copy link
Copy Markdown

ecosystem-analyzer results

Lint rule Added Removed Changed
invalid-argument-type 2 0 0
invalid-key 2 0 0
invalid-assignment 1 0 0
unresolved-attribute 1 0 0
Total 6 0 0

Flaky changes detected. This PR summary excludes flaky changes; see the HTML report for details.

Raw diff:

pandas (https://github.com/pandas-dev/pandas)
+ pandas/core/generic.py:3686:46 error[invalid-argument-type] Argument to bound method `TypedDictFallback.pop` is incorrect: Expected `Never`, found `Literal["__index__"]`
+ pandas/core/generic.py:3687:47 error[invalid-argument-type] Argument to bound method `TypedDictFallback.pop` is incorrect: Expected `Never`, found `Literal["__columns__"]`
+ pandas/core/generic.py:3693:27 error[invalid-assignment] Object of type `(Mapping[str | int, (...) -> Unknown] & Top[dict[Unknown, Unknown]]) | (Mapping[str | int, (...) -> Unknown] & <TypedDict with no items>)` is not assignable to `list[Unknown] | tuple[Unknown, ...] | dict[Unknown, Unknown] | ((...) -> Unknown) | None`
+ pandas/core/generic.py:3697:21 error[unresolved-attribute] Attribute `update` is not defined on `list[Unknown]`, `tuple[Unknown, ...]`, `(...) -> Unknown`, `None` in union `list[Unknown] | tuple[Unknown, ...] | dict[Unknown, Unknown] | ((...) -> Unknown) | None`
+ pandas/io/common.py:1401:23 error[invalid-key] TypedDict `<TypedDict with no items>` can only be subscripted with a string literal key, got key of type `Hashable | str`.

xarray (https://github.com/pydata/xarray)
+ xarray/core/dataset.py:4722:21 error[invalid-key] TypedDict `<TypedDict with no items>` can only be subscripted with a string literal key, got key of type `Hashable`.

Full report with detailed diff (timing results)

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

Labels

ty Multi-file analysis & type inference

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant