I have a function that returns a str or a specified fallback value (str or None). When giving a str fallback, the function is guaranteed to return a str. Running mypy on this however gives an error.
MWE:
from typing import Any
def f(x: str, fallback: str | None) -> str | None:
if x.lower() == "yes":
return "okay"
return fallback
f("no", fallback="").lower()
f.py:10: error: Item "None" of "str | None" has no attribute "lower" [union-attr]
How can I tell mypy that it’s safe to call lower() on the return value of the function?
>Solution :
from typing import overload
@overload
def f(x: str, fallback: str) -> str:
...
@overload
def f(x: str, fallback: str | None = None) -> str | None:
...
def f(x: str, fallback: str | None = None) -> str | None:
if x.lower() == "yes":
return "okay"
return fallback
f("no", fallback="").lower()
f("no").lower()
(type-)checks out:
f("no", fallback="").lower()is fine (since the fallback is passed in and a string)f("no").lower()still saysItem "None" of "str | None" has no attribute "lower"
Of course, if fallback is actually None, this will return a None, so you’ve broken the pinky-promise your types say.