| -- Enforcement of upper bounds |
| -- --------------------------- |
| |
| |
| [case testBoundOnGenericFunction] |
| from typing import TypeVar |
| |
| class A: pass |
| class B(A): pass |
| class C(A): pass |
| class D: pass |
| |
| T = TypeVar('T', bound=A) |
| U = TypeVar('U') |
| def f(x: T) -> T: pass |
| def g(x: U) -> U: |
| return f(x) # E: Value of type variable "T" of "f" cannot be "U" |
| |
| f(A()) |
| f(B()) |
| f(D()) # E: Value of type variable "T" of "f" cannot be "D" |
| |
| b = B() |
| if int(): |
| b = f(b) |
| if int(): |
| b = f(C()) # E: Incompatible types in assignment (expression has type "C", variable has type "B") |
| |
| |
| [case testBoundOnGenericClass] |
| from typing import TypeVar, Generic |
| |
| class A: pass |
| class B(A): pass |
| T = TypeVar('T', bound=A) |
| |
| class G(Generic[T]): |
| def __init__(self, x: T) -> None: pass |
| |
| v: G[A] |
| w: G[B] |
| x: G[str] # E: Type argument "str" of "G" must be a subtype of "A" |
| y = G('a') # E: Value of type variable "T" of "G" cannot be "str" |
| z = G(A()) |
| z = G(B()) |
| |
| |
| [case testBoundVoid] |
| # flags: --no-strict-optional |
| from typing import TypeVar, Generic |
| T = TypeVar('T', bound=int) |
| class C(Generic[T]): |
| t = None # type: T |
| def get(self) -> T: |
| return self.t |
| c1 = None # type: C[None] |
| c1.get() |
| d = c1.get() |
| reveal_type(d) # N: Revealed type is "None" |
| |
| |
| [case testBoundAny] |
| from typing import TypeVar, Generic |
| T = TypeVar('T', bound=int) |
| class C(Generic[T]): |
| def __init__(self, x: T) -> None: pass |
| def f(x: T) -> T: |
| return x |
| |
| def g(): pass |
| |
| f(g()) |
| C(g()) |
| z: C |
| |
| |
| [case testBoundHigherOrderWithVoid] |
| # flags: --no-strict-optional |
| from typing import TypeVar, Callable |
| class A: pass |
| T = TypeVar('T', bound=A) |
| def f(g: Callable[[], T]) -> T: |
| return g() |
| def h() -> None: pass |
| f(h) |
| a = f(h) |
| reveal_type(a) # N: Revealed type is "None" |
| |
| |
| [case testBoundInheritance] |
| from typing import TypeVar, Generic |
| class A: pass |
| T = TypeVar('T') |
| TA = TypeVar('TA', bound=A) |
| |
| class C(Generic[TA]): pass |
| class D0(C[TA], Generic[TA]): pass |
| class D1(C[T], Generic[T]): pass # E: Type argument "T" of "C" must be a subtype of "A" |
| class D2(C[A]): pass |
| class D3(C[str]): pass # E: Type argument "str" of "C" must be a subtype of "A" |
| |
| |
| -- Using information from upper bounds |
| -- ----------------------------------- |
| |
| |
| [case testBoundGenericFunctions] |
| from typing import TypeVar |
| class A: pass |
| class B(A): pass |
| |
| T = TypeVar('T') |
| TA = TypeVar('TA', bound=A) |
| TB = TypeVar('TB', bound=B) |
| |
| def f(x: T) -> T: |
| return x |
| def g(x: TA) -> TA: |
| return f(x) |
| def h(x: TB) -> TB: |
| return g(x) |
| def g2(x: TA) -> TA: |
| return h(x) # Fail |
| |
| def j(x: TA) -> A: |
| return x |
| def k(x: TA) -> B: |
| return x # Fail |
| [out] |
| main:16: error: Value of type variable "TB" of "h" cannot be "TA" |
| main:21: error: Incompatible return value type (got "TA", expected "B") |
| |
| |
| [case testBoundMethodUsage] |
| from typing import TypeVar |
| class A0: |
| def foo(self) -> None: pass |
| class A(A0): |
| def bar(self) -> None: pass |
| a = 1 |
| @property |
| def b(self) -> int: |
| return self.a |
| class B(A): |
| def baz(self) -> None: pass |
| |
| T = TypeVar('T', bound=A) |
| |
| def f(x: T) -> T: |
| x.foo() |
| x.bar() |
| x.baz() # E: "T" has no attribute "baz" |
| x.a |
| x.b |
| return x |
| |
| b = f(B()) |
| [builtins fixtures/property.pyi] |
| [out] |
| |
| [case testBoundClassMethod] |
| from typing import TypeVar |
| class A0: |
| @classmethod |
| def foo(cls, x: int) -> int: pass |
| class A(A0): pass |
| |
| T = TypeVar('T', bound=A) |
| def f(x: T) -> int: |
| return x.foo(22) |
| [builtins fixtures/classmethod.pyi] |
| |
| |
| [case testBoundClassMethodWithNamedTupleBase] |
| from typing import NamedTuple, Type, TypeVar |
| class A(NamedTuple): |
| @classmethod |
| def foo(cls) -> None: ... |
| |
| T = TypeVar('T', bound=A) |
| def f(x: Type[T]) -> None: |
| reveal_type(x.foo) # N: Revealed type is "def ()" |
| x.foo() |
| [builtins fixtures/classmethod.pyi] |
| |
| |
| [case testBoundStaticMethod] |
| from typing import TypeVar |
| class A0: |
| @staticmethod |
| def foo(x: int) -> int: pass |
| class A(A0): pass |
| |
| T = TypeVar('T', bound=A) |
| def f(x: T) -> int: |
| return x.foo(22) |
| [builtins fixtures/staticmethod.pyi] |
| |
| |
| [case testBoundOnDecorator] |
| from typing import TypeVar, Callable, Any, cast |
| T = TypeVar('T', bound=Callable[..., Any]) |
| |
| def twice(f: T) -> T: |
| def result(*args, **kwargs) -> Any: |
| f(*args, **kwargs) |
| return f(*args, **kwargs) |
| return cast(T, result) |
| |
| @twice |
| def foo(x: int) -> int: |
| return x |
| |
| a = 1 |
| b = foo(a) |
| if int(): |
| b = 'a' # E: Incompatible types in assignment (expression has type "str", variable has type "int") |
| twice(a) # E: Value of type variable "T" of "twice" cannot be "int" |
| [builtins fixtures/args.pyi] |
| |
| |
| [case testIterableBoundUnpacking] |
| from typing import Tuple, TypeVar |
| TupleT = TypeVar("TupleT", bound=Tuple[int, ...]) |
| def f(t: TupleT) -> None: |
| a, *b = t |
| reveal_type(a) # N: Revealed type is "builtins.int" |
| reveal_type(b) # N: Revealed type is "builtins.list[builtins.int]" |
| [builtins fixtures/tuple.pyi] |