Using TypeVar
one can annotate functions like:
def f[T: (str, int)](x:T) -> T:
if isinstance(x, int):
return 0
return x
and type checkers will be happy. However it does not work for a None
constraint.
def f[T: (str, None)](x:T) -> T:
if x is None:
return None
return x
gives the error Type "None" is not assignable to type "T@f"
.
Why is this not possible? Is there a way to achieve this with TypeVars
or does one have to use overloads?
Edit: The examples used to say return 'a'
, which would raise a valid error from the type checker. This was an oversight I made while trimming down the example for this question.
Also it is indeed something that I ran into with pyright, not mypy (mypy doesn't complain).
This is either a bug or a limitation of Pyright/Pylance, which is the type checker in question.
x is None
adds a condition on the original type variable T
, causing x
to be of type None*
within the if
block. To oversimplify things, None*
can be understood as "some subtype of None
and T
".
def f[T: (str, None)](x: T) -> T:
if x is None:
reveal_type(x) # None*
return None
reveal_type(x) # str*
return 'a'
In reality, None
/NoneType
is a final type, so its only subtype is itself. Pyright doesn't understand this well enough, and so it wrongly considers None
not to be assignable to T
, which at the time is None*
.