Description
Bug Report
If I have an @overload
ed generic function, and call that function with a variable having union type, I get inconsistent results.
To Reproduce
from typing import TypeVar
from typing import overload
from typing_extensions import assert_type
_T = TypeVar("_T")
@overload
def makelist(a: list[_T]) -> list[_T]: ...
@overload
def makelist(a: _T) -> list[_T]: ...
def makelist(a: list[_T] | _T) -> list[_T]:
if isinstance(a, list):
return a
return [a]
# no error
assert_type(makelist("a"), list[str])
# no error
assert_type(makelist(["a"]), list[str])
arg: str | list[str] = "a"
# error: Expression is of type "list[str | list[str]]", not "list[str]" [assert-type]
assert_type(makelist(arg), list[str])
def want_list_of_str(a: list[str]) -> None:
...
# no error, despite error above
want_list_of_str(makelist(arg))
list_of_str: list[str] = makelist(arg)
Expected Behavior
I expect that if the argument has type str | list[str]
, then makelist(arg)
should be of type list[str]
.
Reason: the first @overload
(def makelist(a: list[_T]) -> list[_T]: ...
) should match list[str]
. The second @overload
(def makelist(a: _T) -> list[_T]: ...
) should match str
. So in all cases of the argument's union type, the return type should be list[str]
.
I also expect mypy to be consistent about expression types. Since mypy says makelist(str_or_list_of_str)
has type list[str | list[str]]
, then list_of_str: list[str] = makelist(str_or_list_of_str)
should raise an error.
Actual Behavior
$ mypy t.py
t.py:23: error: Expression is of type "list[str | list[str]]", not "list[str]" [assert-type]
Your Environment
- Mypy version used:
mypy 1.14.0 (compiled: yes)
- Mypy command-line flags:
mypy
- Mypy configuration options from
pyproject.toml
:
[tool.mypy]
strict = true
- Python version used:
Python 3.10.12
If I had to guess, it seems like mypy is wrongly matching my str | list[str]
argument to the broad overload (def makelist(a: _T) -> list[_T]: ...
). But I don't understand why it's not consistent.
#17331 also involves overloads and generics. Perhaps it's related.