Reputation: 2409
Say I have the following code....
from __future__ import annotations
from typing import Any, TypeVar, Type
from typing_extensions import Literal
_T = TypeVar("_T")
class A:
@classmethod
def constructor(cls: Type[_T]) -> _T:
return cls()
def __enter__(self) -> A:
return self
def __exit__(self, *args: Any, **kwargs: Any) -> Literal[False]:
return False
class B(A):
pass
def test() -> None:
a_instance: A = A.constructor()
b_instance: B = B.constructor() # Works because constructor return type is _T
with B() as b:
a_instance = b
b_instance = b # Error, Incompatible types in assignment
If I run mypy against the above code, I get the following warning
» mypy test.py
test.py:30: error: Incompatible types in assignment (expression has type "A", variable has type "B")
Found 1 error in 1 file (checked 1 source file)
This is because the return type of A.__enter__
, (A
) is inherited by B
, so mypy thinks that B.__enter__
also returns A
. I'd like to have to avoid re-implementing the function just to correct the typehint...
class B(A):
def __enter__(self) -> B:
return super().__enter__() # type: ignore
I've gotten around similar issues in classmethod constructors by using a TypeVar
to template away the type of the cls
so I can use it later, but I'm not sure how a similar trick could apply to non-classmethods.
This situation also applies for any other method that returns self
.
Upvotes: 0
Views: 98
Reputation: 2409
Believe I figured this out...
You need to also use the TypeVar _T
to indicate the type of self
. Then you can re-use that same _T
as the return type.
Note, if you want to use any attributes of the class (like I do in the print
below), you also need to bind the TypeVar
to a class using the bound
argument.
_T = TypeVar("_T", bound="A")
class A:
def __init__(self) -> None:
self.a_attr = 1
@classmethod
def constructor(cls: Type[_T]) -> _T:
return cls()
def __enter__(self: _T) -> _T:
print(self.a_attr)
return self
Upvotes: 1