-
Notifications
You must be signed in to change notification settings - Fork 1.6k
[ty] dict is not assignable to TypedDict
#21238
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
Conversation
|
|
||
| # TODO: This should not error. | ||
| # error: [invalid-assignment] | ||
| persons: list[Person] = [{"name": n} for n in ["Alice", "Bob"]] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@sharkdp it looks like the type context is not being propagated correctly here, and only seemed so because of dict[str, str] being assignable to Person.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, absolutely. I was aware about this, but should have added a comment. But this is why I added the other test below (where it's supposed to be an error).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See also this comment: #20962 (comment)
Diagnostic diff on typing conformance testsChanges were detected when running ty on typing conformance tests--- old-output.txt 2025-11-03 17:12:53.086444331 +0000
+++ new-output.txt 2025-11-03 17:12:56.296455134 +0000
@@ -953,14 +953,16 @@
typeddicts_extra_items.py:293:44: error[invalid-key] Invalid key for TypedDict `ClosedMovie`: Unknown key "year"
typeddicts_extra_items.py:299:54: error[invalid-key] Invalid key for TypedDict `MovieExtraStr`: Unknown key "summary"
typeddicts_extra_items.py:302:54: error[invalid-key] Invalid key for TypedDict `MovieExtraInt`: Unknown key "year"
-typeddicts_extra_items.py:303:1: error[invalid-assignment] Object of type `dict[Unknown | str, Unknown | str | int]` is not assignable to `Mapping[str, int]`
typeddicts_extra_items.py:310:5: error[type-assertion-failure] Argument does not have asserted type `list[tuple[str, int | str]]`
typeddicts_extra_items.py:311:5: error[type-assertion-failure] Argument does not have asserted type `list[int | str]`
typeddicts_extra_items.py:327:5: error[unresolved-attribute] Object of type `IntDict` has no attribute `clear`
typeddicts_extra_items.py:329:52: error[invalid-key] Invalid key for TypedDict `IntDictWithNum`: Unknown key "bar" - did you mean "num"?
+typeddicts_extra_items.py:337:1: error[unresolved-attribute] Object of type `IntDictWithNum` has no attribute `clear`
typeddicts_extra_items.py:339:1: error[type-assertion-failure] Argument does not have asserted type `tuple[str, int]`
+typeddicts_extra_items.py:339:13: error[unresolved-attribute] Object of type `IntDictWithNum` has no attribute `popitem`
typeddicts_extra_items.py:342:27: error[invalid-key] Cannot access `IntDictWithNum` with a key of type `str`. Only string literals are allowed as keys on TypedDicts.
typeddicts_extra_items.py:343:31: error[invalid-key] Invalid key for TypedDict `IntDictWithNum` of type `str`
+typeddicts_extra_items.py:352:5: error[invalid-assignment] Object of type `dict[str, int]` is not assignable to `IntDict`
typeddicts_operations.py:22:17: error[invalid-assignment] Invalid assignment to key "name" with declared type `str` on TypedDict `Movie`: value of type `Literal[1982]`
typeddicts_operations.py:23:17: error[invalid-assignment] Invalid assignment to key "year" with declared type `int` on TypedDict `Movie`: value of type `Literal[""]`
typeddicts_operations.py:24:7: error[invalid-key] Invalid key for TypedDict `Movie`: Unknown key "other"
@@ -969,6 +971,7 @@
typeddicts_operations.py:29:42: error[invalid-argument-type] Invalid argument to key "year" with declared type `int` on TypedDict `Movie`: value of type `float`
typeddicts_operations.py:32:36: error[invalid-key] Invalid key for TypedDict `Movie`: Unknown key "other"
typeddicts_operations.py:37:20: error[missing-typed-dict-key] Missing required key 'name' in TypedDict `Movie` constructor
+typeddicts_operations.py:47:1: error[unresolved-attribute] Object of type `Movie` has no attribute `clear`
typeddicts_operations.py:62:1: error[unresolved-attribute] Object of type `MovieOptional` has no attribute `clear`
typeddicts_readonly.py:24:4: error[invalid-assignment] Cannot assign to key "members" on TypedDict `Band`: key is marked read-only
typeddicts_readonly.py:50:4: error[invalid-assignment] Cannot assign to key "title" on TypedDict `Movie1`: key is marked read-only
@@ -988,5 +991,5 @@
typeddicts_usage.py:28:17: error[missing-typed-dict-key] Missing required key 'name' in TypedDict `Movie` constructor
typeddicts_usage.py:28:18: error[invalid-key] Invalid key for TypedDict `Movie`: Unknown key "title"
typeddicts_usage.py:40:24: error[invalid-type-form] The special form `typing.TypedDict` is not allowed in type expressions. Did you mean to use a concrete TypedDict or `collections.abc.Mapping[str, object]` instead?
-Found 990 diagnostics
+Found 993 diagnostics
WARN A fatal error occurred while checking some files. Not all project files were analyzed. See the diagnostics list above for details. |
|
|
I'm not sure if we want to be emitting |
CodSpeed Performance ReportMerging #21238 will degrade performances by 17.57%Comparing Summary
Benchmarks breakdown
Footnotes
|
I was about to add a comment to this PR that we should maybe try to avoid emitting the additional |
sharkdp
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice, thank you!
| ConstraintSet::from(relation.is_assignability()) | ||
| } | ||
|
|
||
| // A non-`TypedDict` cannot subtype a `TypedDict` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess intersections (of a TypedDict and something else), typevars, and Never would be types that can subtype TypedDict, but they were all handled above...
|
|
||
| let elts = elts.iter().map(|elt| [Some(elt)]); | ||
| self.infer_collection_literal(elts, tcx, KnownClass::Set) | ||
| let infer_elt_ty = |builder: &mut Self, elt, tcx| builder.infer_expression(elt, tcx); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I know you're not the one to invent/introduce this, but I think I would prefer expanding elt/elts to element/elements
|
I added a special case to avoid the duplicated diagnostics for simple annotated assignments, but attribute assignment and function arguments are trickier, because we may hide typed-dict diagnostics during multi-inference and rely on the assignability check to fail. There is one correct new typing comformance diagnostic, but also two regressions because we do not correctly handle |
|
| Lint rule | Added | Removed | Changed |
|---|---|---|---|
invalid-argument-type |
167 | 0 | 0 |
invalid-assignment |
82 | 0 | 32 |
invalid-return-type |
38 | 0 | 0 |
unused-ignore-comment |
1 | 33 | 0 |
non-subscriptable |
0 | 11 | 0 |
possibly-missing-attribute |
4 | 0 | 0 |
invalid-parameter-default |
2 | 0 | 0 |
no-matching-overload |
1 | 1 | 0 |
possibly-missing-implicit-call |
0 | 1 | 0 |
unresolved-attribute |
0 | 1 | 0 |
unsupported-operator |
1 | 0 | 0 |
| Total | 296 | 47 | 32 |
3c6a2c7 to
ee763ef
Compare
ee763ef to
2aa777e
Compare
|
Most of the diagnostics seem to be duplicated diagnostics (in the not-so-simple special case). I opened astral-sh/ty#1472 to track that issue, but I think landing this is high priority because of how much bidirectional-inference relies on the fallback untyped |
* main: (188 commits) [ty] Discover site-packages from the environment that ty is installed in (astral-sh#21286) [ty] Make special cases for `UnionType` slightly narrower (astral-sh#21276) Require ignore 0.4.24 in `Cargo.toml` (astral-sh#21292) [ty] Favour imported symbols over builtin symbols (astral-sh#21285) docs: revise Ruff setup instructions for Zed editor (astral-sh#20935) [ty] Update salsa (astral-sh#21281) [syntax-error]: no binding for nonlocal PLE0117 as a semantic syntax error (astral-sh#21032) [ty] Constraining a typevar with itself (possibly via union or intersection) (astral-sh#21273) [`ruff`] Fix false positives on starred arguments (`RUF057`) (astral-sh#21256) [ty] Simplify unions containing multiple type variables during inference (astral-sh#21275) [ty] Add `ty_server::Db` trait (astral-sh#21241) [ty] Refactor `Range` to/from `TextRange` conversion as prep for notebook support (astral-sh#21230) [ty] Fix playground crash when file name includes path separator (astral-sh#21151) [`refurb`] Fix false negative for underscores before sign in `Decimal` constructor (`FURB157`) (astral-sh#21190) [ty] Allow values of type `None` in type expressions (astral-sh#21263) Run codspeed benchmarks with `profiling` profile (astral-sh#21261) [ty] Update expected diagnostic count in benchmarks (astral-sh#21269) Avoid extra parentheses for long `match` patterns with `as` captures (astral-sh#21176) [ty] Update salsa (astral-sh#21265) [ty] `dict` is not assignable to `TypedDict` (astral-sh#21238) ...
Summary
A lot of the bidirectional inference work relies on
dictnot being assignable toTypedDict, so I think it makes sense to add this before fully implementing astral-sh/ty#1387.