[ty] prefer declared type on invalid TypedDict creation (#21168)

## Summary

In general, when we have an invalid assignment (inferred assigned type
is not assignable to declared type), we fall back to inferring the
declared type, since the declared type is a more explicit declaration of
the programmer's intent. This also maintains the invariant that our
inferred type for a name is always assignable to the declared type for
that same name. For example:

```py
x: str = 1
reveal_type(x)  # revealed: str
```

We weren't following this pattern for dictionary literals inferred (via
type context) as a typed dictionary; if the literal was not valid for
the annotated TypedDict type, we would just fall back to the normal
inferred type of the dict literal, effectively ignoring the annotation,
and resulting in inferred type not assignable to declared type.

## Test Plan

Added mdtest assertions.
This commit is contained in:
Carl Meyer 2025-10-31 11:12:06 -04:00 committed by GitHub
parent 9d7da914b9
commit 1d111c8780
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 17 additions and 17 deletions

View file

@ -99,15 +99,24 @@ eve1a: Person = {"name": b"Eve", "age": None}
# error: [invalid-argument-type] "Invalid argument to key "name" with declared type `str` on TypedDict `Person`"
eve1b = Person(name=b"Eve", age=None)
reveal_type(eve1a) # revealed: Person
reveal_type(eve1b) # revealed: Person
# error: [missing-typed-dict-key] "Missing required key 'name' in TypedDict `Person` constructor"
eve2a: Person = {"age": 22}
# error: [missing-typed-dict-key] "Missing required key 'name' in TypedDict `Person` constructor"
eve2b = Person(age=22)
reveal_type(eve2a) # revealed: Person
reveal_type(eve2b) # revealed: Person
# error: [invalid-key] "Invalid key for TypedDict `Person`: Unknown key "extra""
eve3a: Person = {"name": "Eve", "age": 25, "extra": True}
# error: [invalid-key] "Invalid key for TypedDict `Person`: Unknown key "extra""
eve3b = Person(name="Eve", age=25, extra=True)
reveal_type(eve3a) # revealed: Person
reveal_type(eve3b) # revealed: Person
```
Also, the value types declared in a `TypedDict` affect generic call inference: