diff --git a/mypy/subtypes.py b/mypy/subtypes.py index a7ff37b8a62f..7a6cd2f350d5 100644 --- a/mypy/subtypes.py +++ b/mypy/subtypes.py @@ -883,6 +883,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 04c710af10d1..20c69b82d409 100644 --- a/test-data/unit/check-inference.test +++ b/test-data/unit/check-inference.test @@ -3264,3 +3264,13 @@ from typing import Dict, Iterable, Tuple, Union def foo(x: Union[Tuple[str, Dict[str, int], str], Iterable[object]]) -> None: ... foo(("a", {"a": "b"}, "b")) [builtins fixtures/dict.pyi] + +[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"