@@ -33,13 +33,12 @@ reveal_type(T.__name__) # revealed: Literal["T"]
3333from typing import TypeVar
3434
3535T = TypeVar(" T" )
36- # TODO : no error
3736# error: [invalid-legacy-type-variable]
3837U: TypeVar = TypeVar(" U" )
3938
40- # error: [invalid-legacy-type-variable] "A legacy `typing.TypeVar` must be immediately assigned to a variable"
41- # error: [invalid-type-form] "Function calls are not allowed in type expressions"
42- TestList = list[ TypeVar( " W " )]
39+ # error: [invalid-legacy-type-variable]
40+ tuple_with_typevar = ( " foo " , TypeVar( " W " ))
41+ reveal_type(tuple_with_typevar[ 1 ]) # revealed: TypeVar
4342```
4443
4544### ` TypeVar ` parameter must match variable name
@@ -49,7 +48,7 @@ TestList = list[TypeVar("W")]
4948``` py
5049from typing import TypeVar
5150
52- # error: [invalid-legacy-type-variable] "The name of a legacy `typing.TypeVar` (`Q`) must match the name of the variable it is assigned to (`T`)"
51+ # error: [invalid-legacy-type-variable]
5352T = TypeVar(" Q" )
5453```
5554
@@ -66,6 +65,22 @@ T = TypeVar("T")
6665T = TypeVar(" T" )
6766```
6867
68+ ### No variadic arguments
69+
70+ ``` py
71+ from typing import TypeVar
72+
73+ types = (int , str )
74+
75+ # error: [invalid-legacy-type-variable]
76+ T = TypeVar(" T" , * types)
77+ reveal_type(T) # revealed: TypeVar
78+
79+ # error: [invalid-legacy-type-variable]
80+ S = TypeVar(" S" , ** {" bound" : int })
81+ reveal_type(S) # revealed: TypeVar
82+ ```
83+
6984### Type variables with a default
7085
7186Note that the ` __default__ ` property is only available in Python ≥3.13.
@@ -91,6 +106,11 @@ reveal_type(S.__default__) # revealed: NoDefault
91106
92107### Using other typevars as a default
93108
109+ ``` toml
110+ [environment ]
111+ python-version = " 3.13"
112+ ```
113+
94114``` py
95115from typing import Generic, TypeVar, Union
96116
@@ -122,6 +142,20 @@ reveal_type(T.__constraints__) # revealed: tuple[()]
122142
123143S = TypeVar(" S" )
124144reveal_type(S.__bound__) # revealed: None
145+
146+ from typing import TypedDict
147+
148+ # error: [invalid-type-form]
149+ T = TypeVar(" T" , bound = TypedDict)
150+ ```
151+
152+ The upper bound must be a valid type expression:
153+
154+ ``` py
155+ from typing import TypedDict
156+
157+ # error: [invalid-type-form]
158+ T = TypeVar(" T" , bound = TypedDict)
125159```
126160
127161### Type variables with constraints
@@ -138,6 +172,16 @@ S = TypeVar("S")
138172reveal_type(S.__constraints__) # revealed: tuple[()]
139173```
140174
175+ Constraints are not simplified relative to each other, even if one is a subtype of the other:
176+
177+ ``` py
178+ T = TypeVar(" T" , int , bool )
179+ reveal_type(T.__constraints__) # revealed: tuple[int, bool]
180+
181+ S = TypeVar(" S" , float , str )
182+ reveal_type(S.__constraints__) # revealed: tuple[int | float, str]
183+ ```
184+
141185### Cannot have only one constraint
142186
143187> ` TypeVar ` supports constraining parametric types to a fixed set of possible types...There should
@@ -146,10 +190,19 @@ reveal_type(S.__constraints__) # revealed: tuple[()]
146190``` py
147191from typing import TypeVar
148192
149- # TODO : error: [invalid-type-variable-constraints ]
193+ # error: [invalid-legacy- type-variable]
150194T = TypeVar(" T" , int )
151195```
152196
197+ ### Cannot have both bound and constraint
198+
199+ ``` py
200+ from typing import TypeVar
201+
202+ # error: [invalid-legacy-type-variable]
203+ T = TypeVar(" T" , int , str , bound = bytes )
204+ ```
205+
153206### Cannot be both covariant and contravariant
154207
155208> To facilitate the declaration of container types where covariant or contravariant type checking is
@@ -178,6 +231,66 @@ T = TypeVar("T", covariant=cond())
178231U = TypeVar(" U" , contravariant = cond())
179232```
180233
234+ ### Invalid keyword arguments
235+
236+ ``` py
237+ from typing import TypeVar
238+
239+ # error: [invalid-legacy-type-variable]
240+ T = TypeVar(" T" , invalid_keyword = True )
241+ ```
242+
243+ ``` pyi
244+ from typing import TypeVar
245+
246+ # error: [invalid-legacy-type-variable]
247+ T = TypeVar(" T" , invalid_keyword = True )
248+
249+ ```
250+
251+ ### Constructor signature versioning
252+
253+ #### For ` typing.TypeVar `
254+
255+ ``` toml
256+ [environment ]
257+ python-version = " 3.10"
258+ ```
259+
260+ In a stub file, features from the latest supported Python version can be used on any version:
261+
262+ ``` pyi
263+ from typing import TypeVar
264+ T = TypeVar(" T" , default = int )
265+ ```
266+
267+ But this raises an error in a non-stub file:
268+
269+ ``` py
270+ from typing import TypeVar
271+
272+ # error: [invalid-legacy-type-variable]
273+ T = TypeVar(" T" , default = int )
274+ ```
275+
276+ #### For ` typing_extensions.TypeVar `
277+
278+ ` typing_extensions.TypeVar ` always supports the latest features, on any Python version.
279+
280+ ``` toml
281+ [environment ]
282+ python-version = " 3.10"
283+ ```
284+
285+ ``` py
286+ from typing_extensions import TypeVar
287+
288+ T = TypeVar(" T" , default = int )
289+ # TODO : should not error, should reveal `int`
290+ # error: [unresolved-attribute]
291+ reveal_type(T.__default__) # revealed: Unknown
292+ ```
293+
181294## Callability
182295
183296A typevar bound to a Callable type is callable:
@@ -231,4 +344,71 @@ def constrained(x: T_constrained):
231344 reveal_type(type (x)) # revealed: type[int] | type[str]
232345```
233346
347+ ## Cycles
348+
349+ ### Bounds and constraints
350+
351+ A typevar's bounds and constraints cannot be generic, cyclic or otherwise:
352+
353+ ``` py
354+ from typing import Any, TypeVar
355+
356+ S = TypeVar(" S" )
357+
358+ # TODO : error
359+ T = TypeVar(" T" , bound = list[S])
360+
361+ # TODO : error
362+ U = TypeVar(" U" , list[" T" ], str )
363+
364+ # TODO : error
365+ V = TypeVar(" V" , list[" V" ], str )
366+ ```
367+
368+ However, they are lazily evaluated and can cyclically refer to their own type:
369+
370+ ``` py
371+ from typing import TypeVar, Generic
372+
373+ T = TypeVar(" T" , bound = list[" G" ])
374+
375+ class G (Generic[T]):
376+ x: T
377+
378+ reveal_type(G[list[G]]().x) # revealed: list[G[Unknown]]
379+ ```
380+
381+ ### Defaults
382+
383+ ``` toml
384+ [environment ]
385+ python-version = " 3.13"
386+ ```
387+
388+ Defaults can be generic, but can only refer to earlier typevars:
389+
390+ ``` py
391+ from typing import Generic, TypeVar
392+
393+ T = TypeVar(" T" )
394+ U = TypeVar(" U" , default = T)
395+
396+ class C (Generic[T, U]):
397+ x: T
398+ y: U
399+
400+ reveal_type(C[int , str ]().x) # revealed: int
401+ reveal_type(C[int , str ]().y) # revealed: str
402+ reveal_type(C[int ]().x) # revealed: int
403+ reveal_type(C[int ]().y) # revealed: int
404+
405+ # TODO : error
406+ V = TypeVar(" V" , default = " V" )
407+
408+ class D (Generic[V]):
409+ x: V
410+
411+ reveal_type(D().x) # revealed: V@D
412+ ```
413+
234414[ generics ] : https://typing.python.org/en/latest/spec/generics.html
0 commit comments