I am using pyright in my project to have some type-safety and I stub upon a type problem that I don't understand. It is really easy to fix (using covariant=True
in the TypeVar) but I don't even understand why is it a problem itself.
Here is an example of my problem:
from typing import TypeVar, Callable, Generic
class A:
...
# Any subclass of A or A that is needed in a Generic
AT = TypeVar("AT", bound=A)
# Generic T where T is a subclass of A or A
class B(Generic[AT]):
...
# Decorator that converts a method of subclass of A (or A) to an instance of B[AT]
def to_b_instance(method: Callable[[AT], Any]) -> B[AT]:
return B()
# Decorator that takes a predicate B[AT]->bool and an instance of B[AT]
def check_b_instance(predicate: Callable[[B[AT]], bool]) -> Callable[[B[AT]], Any]:
def inner(b: B[AT]):
...
return inner
# A predicate that can check B[SubA]'s instances
def my_predicate(b: B["SubA"]):
return True
class SubA(A):
@check_b_instance(my_predicate) # <- The problem is displayed here (full error bellow)
@to_b_instance
def methode(self):
pass
Argument of type "B[Self@SubA]" cannot be assigned to parameter of type "B[SubA]"
"B[Self@SubA]" is incompatible with "B[SubA]"
TypeVar "AT@B" is invariant
Type "Self@SubA" cannot be assigned to type "SubA" Pyright(reportGeneralTypeIssues)
Firstly this error is not raised by mypy, making me confident that this is a bug on pyright side.
I know that I can fix the problem by changing the AT TypeVar TypeVar("AT", bound=A, covariant=True)
but I just don't understand how Self@SubA
is different than SubA
and what rule forces me to do so.
By the way, explicitly typing the self
parameter with SubA
seems to fix the problem too, without adding any additional error. I really don't get it.
# AT is not covariant
class SubA(A):
@check_b_instance(my_predicate)
@to_b_instance
def methode(self: "SubA"): # <- No error anymore
pass