Description
When overload matching is ambiguous due to an Any
argument, mypy typically looks at the return types of the potential matches. If the return types differ, it evaluates the return type as Any
to preserve the "gradual type guarantee". It apparently doesn't do this when the return types of the potential overload matches use TypeIs
. See the example below, where mypy matches the first overload rather than detecting the ambiguity and evaluating Any
.
from typing import Any, overload
from typing_extensions import TypeIs
@overload
def func1(x: str) -> TypeIs[str]:
...
@overload
def func1(x: int) -> TypeIs[int]:
...
def func1(x: Any) -> Any:
return True
def func2(val: Any):
if func1(val):
reveal_type(val) # Should be Any
I discovered this problem because pyright has the same issue. I'm guessing that the underlying cause of the bug in mypy is similar. Pyright's logic was treating TypeIs[T]
as equivalent to bool
in this case, but TypeIs[T]
should be treated as a subtype of bool
. Also, TypeIs[X]
is not equivalent to TypeIs[Y]
unless X
is equivalent to Y
.
This bug affects a recent change in typeshed to the dataclasses.isdataclass
function, as discussed here.
Activity
sobolevn commentedon Jul 24, 2024
The same happens with
TypeGuard
as well:[-]Overloads not resolved correctly when argument is `Any` and return types use `TypeIs`[/-][+]Overloads not resolved correctly when argument is `Any` and return types use `TypeGuard` or `TypeIs`[/+]sobolevn commentedon Jul 24, 2024
The problem is that we don't check for overloads here:
mypy/mypy/checker.py
Lines 5873 to 5890 in db9837f
erictraut commentedon Jul 24, 2024
The same happens if
TypeGuard
andTypeIs
are both used in an overload. This is an edge case that's unlikely to ever be used in real-world code, but it might as well be fixed along with the other cases.is_dataclass(Any)
return type incorrect, differs fromis_dataclass(object)
python/typeshed#12401is_dataclass(Any)
python/typeshed#12517sobolevn commentedon Aug 13, 2024
I have a prototype implementation, will create a PR tomorrow 🎉
Type[Guard | Is]
#17678Infer correct types with overloads of `Type[Guard | Is]` (python#17678)
davidhalter commentedon Feb 11, 2025
There is an inconsistency left where currently
TypeIs/TypeGuard
narrows in case of multi-Any matches, while this is not done in normal overloads. I find this strange and would prefer that it is consistent.Playground (used Mypy 1.15)
(Edited, because I messed up)
Fine
sobolevn commentedon Feb 11, 2025
@davidhalter this is unrelated, please create a new issue (if there no existing one).
davidhalter commentedon Feb 11, 2025
@sobolevn I created #18659
Why is this unrelated? Before this change it would narrow to the first overload. Now it narrows to a union. The consistent way would have been not narrowing at all.
Infer correct types with overloads of `Type[Guard | Is]` (python#17678)
sterliakov commentedon May 29, 2025
The first attempt was reverted due to an unexpected regression in #19161, reopening.