[ty] Reduce false positives for TypedDict types (#19354)

This commit is contained in:
Alex Waygood 2025-07-15 12:47:19 +01:00 committed by GitHub
parent f3a27406c9
commit 002f9057db
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 34 additions and 11 deletions

View file

@ -245,7 +245,7 @@ class D(TypedDict):
td = D(x=1, label="a")
td["x"] = 0
# TODO: should be Literal[0]
reveal_type(td["x"]) # revealed: @Todo(TypedDict)
reveal_type(td["x"]) # revealed: @Todo(Support for `TypedDict`)
# error: [unresolved-reference]
does["not"]["exist"] = 0

View file

@ -10,8 +10,6 @@ class Person(TypedDict):
name: str
age: int | None
# TODO: This should not be an error:
# error: [invalid-assignment]
alice: Person = {"name": "Alice", "age": 30}
# Alternative syntax
@ -22,6 +20,6 @@ msg = Message(id=1, content="Hello")
# No errors for yet-unsupported features (`closed`):
OtherMessage = TypedDict("OtherMessage", {"id": int, "content": str}, closed=True)
reveal_type(Person.__required_keys__) # revealed: @Todo(TypedDict)
reveal_type(Message.__required_keys__) # revealed: @Todo(TypedDict)
reveal_type(Person.__required_keys__) # revealed: @Todo(Support for `TypedDict`)
reveal_type(Message.__required_keys__) # revealed: @Todo(Support for `TypedDict`)
```

View file

@ -5880,6 +5880,9 @@ pub enum DynamicType {
/// A special Todo-variant for type aliases declared using `typing.TypeAlias`.
/// A temporary variant to detect and special-case the handling of these aliases in autocomplete suggestions.
TodoTypeAlias,
/// A special Todo-variant for classes inheriting from `TypedDict`.
/// A temporary variant to avoid false positives while we wait for full support.
TodoTypedDict,
}
impl DynamicType {
@ -5911,6 +5914,13 @@ impl std::fmt::Display for DynamicType {
f.write_str("@Todo")
}
}
DynamicType::TodoTypedDict => {
if cfg!(debug_assertions) {
f.write_str("@Todo(Support for `TypedDict`)")
} else {
f.write_str("@Todo")
}
}
}
}
}

View file

@ -985,7 +985,7 @@ impl<'db> Bindings<'db> {
},
Type::SpecialForm(SpecialFormType::TypedDict) => {
overload.set_return_type(todo_type!("TypedDict"));
overload.set_return_type(todo_type!("Support for `TypedDict`"));
}
// Not a special case

View file

@ -21,7 +21,7 @@ use crate::types::signatures::{CallableSignature, Parameter, Parameters, Signatu
use crate::types::tuple::TupleType;
use crate::types::{
BareTypeAliasType, Binding, BoundSuperError, BoundSuperType, CallableType, DataclassParams,
KnownInstanceType, TypeAliasType, TypeMapping, TypeRelation, TypeTransformer,
DynamicType, KnownInstanceType, TypeAliasType, TypeMapping, TypeRelation, TypeTransformer,
TypeVarBoundOrConstraints, TypeVarInstance, TypeVarKind, infer_definition_types,
};
use crate::{
@ -415,6 +415,15 @@ impl<'db> ClassType<'db> {
other: Self,
relation: TypeRelation,
) -> bool {
// TODO: remove this branch once we have proper support for TypedDicts.
if self.is_known(db, KnownClass::Dict)
&& other
.iter_mro(db)
.any(|b| matches!(b, ClassBase::Dynamic(DynamicType::TodoTypedDict)))
{
return true;
}
self.iter_mro(db).any(|base| {
match base {
ClassBase::Dynamic(_) => match relation {

View file

@ -51,7 +51,8 @@ impl<'db> ClassBase<'db> {
ClassBase::Dynamic(
DynamicType::Todo(_)
| DynamicType::TodoPEP695ParamSpec
| DynamicType::TodoTypeAlias,
| DynamicType::TodoTypeAlias
| DynamicType::TodoTypedDict,
) => "@Todo",
ClassBase::Protocol => "Protocol",
ClassBase::Generic => "Generic",
@ -229,7 +230,7 @@ impl<'db> ClassBase<'db> {
SpecialFormType::OrderedDict => {
Self::try_from_type(db, KnownClass::OrderedDict.to_class_literal(db))
}
SpecialFormType::TypedDict => Self::try_from_type(db, todo_type!("TypedDict")),
SpecialFormType::TypedDict => Some(Self::Dynamic(DynamicType::TodoTypedDict)),
SpecialFormType::Callable => {
Self::try_from_type(db, todo_type!("Support for Callable as a base class"))
}

View file

@ -6508,7 +6508,8 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
todo @ Type::Dynamic(
DynamicType::Todo(_)
| DynamicType::TodoPEP695ParamSpec
| DynamicType::TodoTypeAlias,
| DynamicType::TodoTypeAlias
| DynamicType::TodoTypedDict,
),
_,
_,
@ -6518,7 +6519,8 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
todo @ Type::Dynamic(
DynamicType::Todo(_)
| DynamicType::TodoPEP695ParamSpec
| DynamicType::TodoTypeAlias,
| DynamicType::TodoTypeAlias
| DynamicType::TodoTypedDict,
),
_,
) => Some(todo),

View file

@ -253,6 +253,9 @@ fn dynamic_elements_ordering(left: DynamicType, right: DynamicType) -> Ordering
(DynamicType::TodoTypeAlias, _) => Ordering::Less,
(_, DynamicType::TodoTypeAlias) => Ordering::Greater,
(DynamicType::TodoTypedDict, _) => Ordering::Less,
(_, DynamicType::TodoTypedDict) => Ordering::Greater,
}
}