[ty] dict is not assignable to TypedDict#21238
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.
@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.
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).
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 |
| ConstraintSet::from(relation.is_assignability()) | ||
| } | ||
|
|
||
| // A non-`TypedDict` cannot subtype a `TypedDict` |
There was a problem hiding this comment.
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.
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.