OrenIshShalom
OrenIshShalom

Reputation: 7162

Mypy errors in Generic class

I slightly modified the documented mypy generic example but got an error:

from typing import Generic, TypeVar, Union

T = TypeVar("T", bound=Union[int, float])

class Person(Generic[T]):

    def __init__(self, salary: T) -> None:
        self.salary: T = salary

    def get_yearly_bonus(self, amount: T) -> None:
        self.salary += amount

    def __str__(self) -> str:
        return f"Person(salary={self.salary})"

p = Person[int](9)
p.get_yearly_bonus(6)
print(p)

It seems mypy fails to recognize the fact that self.salary and amount must be of the same type.

$ mypy test.py 
test.py:11: error: Incompatible types in assignment (expression has type "float", variable has type "T")
test.py:11: error: Unsupported operand types for + (likely involving Union)
Found 2 errors in 1 file (checked 1 source file)

Upvotes: 1

Views: 786

Answers (1)

user2357112
user2357112

Reputation: 281758

The problem isn't mypy failing to recognize that self.salary and amount share a type. mypy knows they both have type T, whatever T is.

The problem is that T isn't guaranteed to be either int or float. T can be any subtype of Union[int, float]. It could be int, or float, or any subclass of those classes, like bool, or it could even be Union[int, float]!

For a sum of two objects of type T, mypy cannot assume the sum has type T. The most specific type mypy can deduce for the sum (accounting for mypy's weird special case that treats int as a subtype of float) is float, which isn't assignable to self.salary.

If you wanted T to be either int or float, a union bound isn't the way to do that. The syntax for "either int or float" is

T = TypeVar('T', int, float)

With T specified this way, mypy can type-check Person against both possible types and see that the class is valid both ways.

Upvotes: 6

Related Questions