Could someone explain why this does not typecheck? Is this a legit error or a limitation in the type checkers? Is there a way to make this work?
from typing import TypeVar, Union
class A:...
class B:...
# A generic function
P = TypeVar('P', A, B)
def dostuff(value: P) -> P:
return value
# Another function that returns any of the types that constrain P
U = Union[A, B]
def make_object() -> U:
return A()
dostuff(A()) # OK
dostuff(B()) # OK
obj = make_object()
dostuff(obj) # Does not typecheck
mypy
Value of type variable "P" of "dostuff" cannot be "Union[A, B]" [type-var]
pyright
Argument of type "U" cannot be assigned to parameter "value" of type "P@dostuff" in function "dostuff"
Type "U" is incompatible with constrained type variable "P"
Constrained typevars require that the value has to be exactly one of the types, it doesn’t allow subclasses, while unions do. Either mark both classes with @final, or make the typevar bound=U.
The constrained type variable means that when you call dostuff, you can do so with an A value (and P will be bound to A), and you can do so with a B value (and P will be bound to B). What you can’t do is provide a value that has unknown type, even if it’s one of A or B without being certain which it is (which is what A | B means).
Strictly speaking, run-time type identification isn’t an available operation. If you only know a value has type A|B, the only operations available are the ones available for type Aand for type B.
One way to think of this is that dostuff has type Callable[[A], A] | Callable[[B], B]. You get to see the type of obj before deciding which half of the union to use, but you don’t get to look at the value of obj before deciding.
Strictly speaking, run-time type identification isn’t an available operation. If you only know a value has type A|B , the only operations available are the ones available for type Aand for type B .
I don’t think there is runtime involved, this is about statically introspecting the provided types. The question I think is: is there a situation under which allowing for this would cause a runtime error? I don’t see how.
There may not be a problem at runtime, but that doesn’t mean the type system can accurately describe the situation. In isolation, you might be able to say that T’s type doesn’t matter and that a union of its constraints would be OK.
Good example! It shows why accepting it would be unsafe for multiple inputs.
I wish it were tolerated in some special cases as the one I shared, but I can see why they did not go for it.