Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
6 changes: 2 additions & 4 deletions crates/ty_python_semantic/resources/mdtest/typed_dict.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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`)
```
10 changes: 10 additions & 0 deletions crates/ty_python_semantic/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5884,6 +5884,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 {
Expand Down Expand Up @@ -5915,6 +5918,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")
}
}
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion crates/ty_python_semantic/src/types/call/bind.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
11 changes: 10 additions & 1 deletion crates/ty_python_semantic/src/types/class.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::{
Expand Down Expand Up @@ -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 {
Expand Down
5 changes: 3 additions & 2 deletions crates/ty_python_semantic/src/types/class_base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -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"))
}
Expand Down
6 changes: 4 additions & 2 deletions crates/ty_python_semantic/src/types/infer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6509,7 +6509,8 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
todo @ Type::Dynamic(
DynamicType::Todo(_)
| DynamicType::TodoPEP695ParamSpec
| DynamicType::TodoTypeAlias,
| DynamicType::TodoTypeAlias
| DynamicType::TodoTypedDict,
),
_,
_,
Expand All @@ -6519,7 +6520,8 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
todo @ Type::Dynamic(
DynamicType::Todo(_)
| DynamicType::TodoPEP695ParamSpec
| DynamicType::TodoTypeAlias,
| DynamicType::TodoTypeAlias
| DynamicType::TodoTypedDict,
),
_,
) => Some(todo),
Expand Down
3 changes: 3 additions & 0 deletions crates/ty_python_semantic/src/types/type_ordering.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
}
}

Expand Down
Loading