Search code examples
pythonmypy

mypy type hint for function that returns both None and string


I have two functions like this. How would I type hint func() such that mypy does not raise an error?

Right now it says

test.py:14: error: Argument 1 to "len" has incompatible type "str | None"; expected "Sized"  [arg-type]
from typing import Optional

path = "path"

def func(path: str) -> Optional[str]:
    s = "foo"
    if True:
        return s
    return None

def func2() -> None:
    if func(path) is not None:
        if len(func(path)) > 0:
            print("Yes")
        else:
            print("No") 

I tried Optional[str] and Union but neither seems to work.


Solution

  • mypy can't know that both invocations of func will yield the same result. It therefor can't know that if func(path) is not None the first time, the second time func(path) is called in len(func(path)) the return value being None is not possible. mypy is correct in this conservative assumption.

    You can bind the return value to a variable, which allows mypy to reason about the possible types of that variable across the type-narrowing if.

    def func2() -> None:
        foo = func(path)
        # `foo` can only be `None` or a `str`; but `foo` in `len(foo)` can't be None.
        if foo is not None and len(foo) > 0:
            print("Yes")
            return
        print("No") 
    

    As a side-note: The expression foo is not None and len(foo) > 0 can be shortened to just foo. Both None and the empty string evaluate false-ish.