Aditya Mishra
Aditya Mishra

Reputation: 1875

TypeChecking for a function that returns None

Consider the recursive implementation of factorial function -

from typing import Optional

def factorial(val: int) -> Optional[int]:
    if val<0:
        return None
    if val==0:
        return 1
    return val*factorial(val-1)


if __name__ == "__main__":
    print(square_root(3))

I am using mypy for static type checking. It throws me the following error -

type-hints.py:8: error: Unsupported operand types for * ("int" and "None")
type-hints.py:8: note: Right operand is of type "Optional[int]"
Found 1 error in 1 file (checked 1 source file)

I tried using Optional as per this stackoverflow question. But it doesn't seem to work. Any suggestions?

Questions -

  1. How to specify return type when the function returns None?
  2. Seems a bit surprising to me that mypy was able to envision a situation where multiplication between int and None might occur. For example - If I remove int for val argument and call the factorial function with a float, it might throw such error.

Upvotes: 3

Views: 988

Answers (1)

MisterMiyagi
MisterMiyagi

Reputation: 50076

MyPy is correct that the function is not well-typed. The signature of the function is (int) -> Optional[int], making val*factorial(val-1) possibly erroneous. That None only occurs for val < 0 is not static type information.

Instead of returning None for invalid input, raise an exception. This makes the function statically well-typed, without removing the error handling.

def factorial(val: int) -> int:
    if val<0:
        raise ValueError("factorial() not defined for negative values")
    if val==0:
        return 1
    return val*factorial(val-1)

If returning None is required for some reason, explicitly annotate that the expression is sound and does not need checking.

def factorial(val: int) -> int:
    if val<0:
        return None
    if val==0:
        return 1
    return val*factorial(val-1)  # type: ignore

Upvotes: 3

Related Questions