Take the following example of code:
from typing import Literal, overload, Tuple
SpecificStrings = Literal['bacon', 'eggs']
@overload
def my_func(foo: SpecificStrings) -> float:
...
@overload
def my_func(foo: str) -> bool:
...
def my_func(foo):
if foo in ('bacon', 'eggs'):
return 101.5
else:
return bool(foo)
def main() -> Tuple[float, bool]:
x = my_func('bacon') # float
y = my_func('any other string') # bool
return x, y
The basic idea is that if my_func
is called with a specific string 'bacon'
or 'eggs'
the return type should be a float
. If any other string is used, the return type should be bool
.
Intuitively, I would expect the more specific annotation (foo: SpecificStrings
) to be checked first before the less specific : str
annotation.
However, when type checking this file with mypy
, I receive an error:
t.py:6: error: Overloaded function signatures 1 and 2 overlap with incompatible return types
Additionally, when type checking with pyright
, there is no errors found.
It seems this could be a bug in mypy
, a bug in pyright
, or possibly something else... PEP484 doesn't seem to indicate what the behavior should be in this scenario.
Is there another way to typehint this scenario such that it works (that is, such that x
and y
in the above example are understood as their respective types float
and bool
) with mypy
?
Edit:
Interesting enough, using Enum
instead of Literal
seems to be a workaround:
from enum import Enum
class SpecificStrings(Enum):
bacon = 'bacon'
eggs = 'eggs'
Using reveal_type()
with pyright and mypy show the correct types in this scenario.
# pyright
C:\Users\path\to\t.py
C:\Users\path\to\t.py:38:13 - information: Type of "x" is "float"
C:\Users\path\to\t.pyt.py:39:13 - information: Type of "y" is "bool"
0 errors, 0 warnings, 2 informations
# mypy
t.py:38: note: Revealed type is "builtins.float"
t.py:39: note: Revealed type is "builtins.bool"
To me, this begs another question: why does Enum
work but not Literal
?
Frustratingly, however, PyCharm does not do the same thing: