Here is the sample code
from typing import Dict, Union, Tuple
def select_range(data: Dict[str, Union[str, int]]) -> Tuple[int, int]:
if data['start'] and data['end']:
return data['start'], data['end']
return 1, 1
select_range({})
Mypy output:
mypy different_return.py
different_return.py:6: error: Incompatible return value type (got
"Tuple[Union[str, int], Union[str, int]]", expected "Tuple[int, int]")
Even though one of the dictionary values is int
, mypy is unable to infer that.
Even though one of the dictionary values is int, mypy is unable to infer that.
Mypy is correct. Your code has a bug and mypy is correctly flagging it. There is no guarantee in your code that data['start']
and data['end']
are always going to be integers.
Your data
signature is Dict[str, Union[str, int]]
, so the values have the type Union[str, int]
. Mypy must assume that it is always correct to pass in {'start': '2018-07-12', 'end': -42}
, so the return value must be Tuple[Union[str, int], Union[str, int]]
. Your claim that the function returns Tuple[int, int]
clashes with this.
It doesn't matter what actually happens at runtime. That's not the point; mypy is a static typechecker, and is designed to help keep your runtime behaviour bug-free. What matters here is that, according to the type hints, it is possible to pass in non-integer values for start
and end
, so the typechecker can't protect you from future bugs in your code that accidentally set a string value for either of those two keys.
If you are passing around structured data in dictionaries, you will always have to fight mypy over this, as dictionaries are really the wrong structure for this. You really want to use a named tuple or a dataclass here.
I'm using the name FooBar
here, but for your specific application I'm sure there will be a better name for the data structure you are passing around:
from typing import NamedTuple
class FooBar(NamedTuple):
start: int
end: int
# other fields, perhaps with defaults and Optionals
def select_range(data: FooBar) -> Tuple[int, int]:
if data.start and data.end:
return data.start, data.end
return 1, 1
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With