diff --git a/mypy/subtypes.py b/mypy/subtypes.py index bc35b1a4d683..d721b6d9be2a 100644 --- a/mypy/subtypes.py +++ b/mypy/subtypes.py @@ -893,6 +893,10 @@ def visit_type_type(self, left: TypeType) -> bool: if isinstance(right, TypeType): return self._is_subtype(left.item, right.item) if isinstance(right, CallableType): + if self.proper_subtype and not right.is_type_obj(): + # We can't accept `Type[X]` as a *proper* subtype of Callable[P, X] + # since this will break transitivity of subtyping. + return False # This is unsound, we don't check the __init__ signature. return self._is_subtype(left.item, right.ret_type) if isinstance(right, Instance): diff --git a/test-data/unit/check-inference.test b/test-data/unit/check-inference.test index 9ee8b3989de8..6767f1c7995c 100644 --- a/test-data/unit/check-inference.test +++ b/test-data/unit/check-inference.test @@ -3372,3 +3372,13 @@ class B: [out] tmp/b.py:2: note: Revealed type is "builtins.int" tmp/a.py:2: note: Revealed type is "builtins.int" + +[case testUnionTypeCallableInference] +from typing import Callable, Type, TypeVar, Union + +class A: + def __init__(self, x: str) -> None: ... + +T = TypeVar("T") +def type_or_callable(value: T, tp: Union[Type[T], Callable[[int], T]]) -> T: ... +reveal_type(type_or_callable(A("test"), A)) # N: Revealed type is "__main__.A"