I'm trying to implement an interface for conversions between types but I'm struggling to make it consistent since typing.Type is covariant
U = TypeVar('U')
class Into(Protocol[U]):
@abstractmethod
def into(self, t: Type[U]) -> U:
pass
The docs give a similar example with a crucial difference
class User: ...
class BasicUser(User): ...
class ProUser(User): ...
class TeamUser(User): ...
def make_new_user(user_class: Type[User]) -> User:
return user_class()
There they say type checkers should check that all subclasses of User should implement a constructor with a valid signature to be instantiated like this. My use case is different because I might not be constructing the new type, just returning a pre-existing one. Say I do
class X: pass
class Wrapper:
def __init__(self, x: X):
self._x = x
def into(self, t: Type[X]) -> X:
return self._x
that all works fine until someone subclasses X
w = Wrapper(X())
...
class XX(X): pass
x: XX = w.into(XX)
The RHS is fine by mypy cos Type is covariant, but clearly the API is broken cos an X isn't an XX. If Type wasn't covariant this wouldn't be a concern: the RHS wouldn't type check until Wrapper was updated to support XX.
My question is: Is there some way to achieve this (or something similar) given the covariance of Type?
Context
I want to use this to convert a type to multiple other types, specifying the desired type explicitly rather than just into_X, into_Y etc. I expect to do this with TypeVar or overload. I'm also having difficulty there.
This is inspired by rust's Into, where t: Type[U] is a type parameter not a function argument.