Note: this question refers to Python 3.12+
Suppose I have:
from typing import Any, TypeVar
import numpy as np
T = TypeVar("T")
U = TypeVar("U")
ListLike = T | list[T] | tuple[T, ...] | np.ndarray[Any, U]
ListLikeStr = ListLike[str, np.object_]
# ListLikeStr should be: str | list[str] | tuple[str, ...] | np.ndarray[Any, np.object_]
This works, but it was lucky. I could have instead written: ListLike[np.object_, str]
, and then I'd get ListLikeStr
being np.object_ | list[np.object_] | tuple[np.object_, ...] | np.ndarray[Any, str]
, which is not what I'd like.
Ideally I could have done something like: ListLike[T=str, U=np.object_]
, but that does not work. So what determines the order when I am instantiating the type variables in ListLike
? How does ListLike
"know" that T
corresponds with str
and U
with np.object_
, when I write ListLike[str, np.object_]
?
In a "traditional" type alias, whichever is referenced first goes first.
PEP 695 type
statements were created to fix this:
(playground: Pyright)
type A[U: str, T] = T | list[T] | tuple[T, U]
a: A[int, str] # error: "int" is not a subtype of "str"
b: A[str, int] # fine
reveal_type(b) # str | list[str] | tuple[int, str]
Mypy have yet to add support for it, but you can import TypeAliasType
from typing_extensions
to achieve the same result (all Python versions):
from typing import TypeVar
from typing_extensions import TypeAliasType
T = TypeVar('T')
U = TypeVar('U', bound = str)
A = TypeAliasType('A', T | list[T] | tuple[T, U], type_params = (U, T))
a: A[int, str] # error: "int" is not a subtype of "str"
b: A[str, int] # fine
reveal_type(b) # int | list[int] | tuple[int, str]