Working with type stubs, I’m wondering if it’s possible to express a type in Python that allows you to type this correctly for any number of arguments:
JavaScript
x
3
1
def test(*args):
2
return args
3
At first glance, I came with:
JavaScript
1
4
1
T = TypeVar('T')
2
def test(*args: T) -> Tuple[T, ]:
3
return args
4
But this of course will only type correctly the first T.
Is the only possible way to write the overrides for all arities?
JavaScript
1
17
17
1
T1 = TypeVar('T1')
2
T2 = TypeVar('T2')
3
T3 = TypeVar('T3')
4
T4 = TypeVar('T4')
5
6
@overload
7
def test(arg1: T1) -> Tuple[T1]:
8
@overload
9
def test(arg1: T1, arg2: T2) -> Tuple[T1, T2]:
10
@overload
11
def test(arg1: T1, arg2: T2, arg3: T3) -> Tuple[T1, T2, T3]:
12
@overload
13
def test(arg1: T1, arg2: T2, arg3: T3, arg4: T4) -> Tuple[T1, T2, T3, T4]:
14
# etc
15
def test(*args: Any) -> Tuple[Any, ]:
16
return args
17
This is not complete either, since it does not carry enough type information to type something like:
JavaScript
1
2
1
x: Tuple[int, int, str] = test(*[1, 2, "4"])
2
Advertisement
Answer
This can solved through TypeVarTuple
from PEP646, implemented in Python 3.11 or forward compat module typing-extensions.
See this analog example:
JavaScript
1
27
27
1
from __future__ import annotations
2
3
from typing import Any, Callable, Generic
4
from typing_extensions import TypeVarTuple, Unpack # typing_extensions only needed for Python < 3.11
5
6
Ts = TypeVarTuple("Ts")
7
8
class Signal(Generic[Unpack[Ts]]):
9
def add_callback(self, func: Callable[[Unpack[Ts]], Any]) -> None:
10
11
12
def emit(self, *args: Unpack[Ts]) -> None:
13
14
15
def callback(a: int, b: str) -> None:
16
17
18
def callback_bad(a: str) -> None:
19
20
21
sig: Signal[int, str] = Signal()
22
sig.add_callback(callback) # Good lint
23
sig.add_callback(callback_bad) # Bad lint
24
25
sig.emit(1223, "foo") # Good lint
26
sig.emit("bar") # Bad lint
27
Also see Dynamic TypeVar for a sequence of types for a solution.