Closed
Description
from typing import Optional, TypeVar
U = TypeVar('U', bound=Optional[int])
def f(u: U) -> U:
if u is None:
return None
assert isinstance(u, int) # removing this line causes it to pass.
return u
Gives
main.py:10: error: Incompatible return value type (got "int", expected "U")
I cannot remove the assertion since it stands in for other code that is effectively constraining u
.
This can be expressed tediously using overload
:
from typing import Optional, TypeVar, overload
@overload
def f(u: None) -> None: ...
@overload
def f(u: int) -> int: ...
def f(u: Optional[int]) -> Optional[int]:
if u is None:
return None
assert isinstance(u, int)
return u
Would it be possible to get the TypeVar
approach to pass? It is far more succinct, and this quickly blows up in complexity if there are more argument-return type constraints to be enforced.
This pattern is analogous to the common class factory pattern:
class C: pass
class B(C): pass
class A(B): pass
T = TypeVar('T', bound=C)
def factory(cls: Type[T]) -> T:
return cls()
Since A < B < C, this enforces
factory(Type[C]) -> C
factory(Type[B]) -> B
factory(Type[A]) -> A
This issue argues that since int < Optional[int] and None < Optional[int], we should ideally be able to similarly specify that
f(int) -> int
f(None) -> None
using the same notation.