diff --git a/crates/ty_python_semantic/resources/mdtest/type_properties/is_subtype_of.md b/crates/ty_python_semantic/resources/mdtest/type_properties/is_subtype_of.md index 51756bac64cf6..5726ce83f79ff 100644 --- a/crates/ty_python_semantic/resources/mdtest/type_properties/is_subtype_of.md +++ b/crates/ty_python_semantic/resources/mdtest/type_properties/is_subtype_of.md @@ -1774,6 +1774,25 @@ static_assert(is_subtype_of(type[B], Callable[[str], B])) static_assert(not is_subtype_of(type[B], Callable[[int], B])) ``` +### Dataclasses + +Dataclasses synthesize a `__init__` method. + +```py +from typing import Callable +from ty_extensions import TypeOf, static_assert, is_subtype_of +from dataclasses import dataclass + +@dataclass +class A: + x: "A" | None + +static_assert(is_subtype_of(type[A], Callable[[A], A])) +static_assert(is_subtype_of(type[A], Callable[[None], A])) +static_assert(is_subtype_of(type[A], Callable[[A | None], A])) +static_assert(not is_subtype_of(type[A], Callable[[int], A])) +``` + ### Bound methods ```py diff --git a/crates/ty_python_semantic/src/types/class.rs b/crates/ty_python_semantic/src/types/class.rs index b013a39b908e4..ff61cc3b1b2ac 100644 --- a/crates/ty_python_semantic/src/types/class.rs +++ b/crates/ty_python_semantic/src/types/class.rs @@ -661,27 +661,33 @@ impl<'db> ClassType<'db> { // same parameters as the `__init__` method after it is bound, and with the return type of // the concrete type of `Self`. let synthesized_dunder_init_callable = - if let Place::Type(Type::FunctionLiteral(dunder_init_function), _) = - dunder_init_function_symbol - { - let synthesized_signature = |signature: Signature<'db>| { - Signature::new(signature.parameters().clone(), Some(correct_return_type)) - .bind_self() + if let Place::Type(ty, _) = dunder_init_function_symbol { + let signature = match ty { + Type::FunctionLiteral(dunder_init_function) => { + Some(dunder_init_function.signature(db)) + } + Type::Callable(callable) => Some(callable.signatures(db)), + _ => None, }; - let synthesized_dunder_init_signature = CallableSignature::from_overloads( - dunder_init_function - .signature(db) - .overloads - .iter() - .cloned() - .map(synthesized_signature), - ); - Some(Type::Callable(CallableType::new( - db, - synthesized_dunder_init_signature, - true, - ))) + if let Some(signature) = signature { + let synthesized_signature = |signature: &Signature<'db>| { + Signature::new(signature.parameters().clone(), Some(correct_return_type)) + .bind_self() + }; + + let synthesized_dunder_init_signature = CallableSignature::from_overloads( + signature.overloads.iter().map(synthesized_signature), + ); + + Some(Type::Callable(CallableType::new( + db, + synthesized_dunder_init_signature, + true, + ))) + } else { + None + } } else { None };