6

How would I use TypeVarTuple for this example?

T = TypeVar("T")
Ts = TypeVarTuple("Ts")

@dataclass
class S(Generic[T]):
    data: T

def data_from_s(*structs: ??) -> ??: 
    return tuple(x.data for x in structs)

a = data_from_s(S(1), S("3"))  # is type tuple[int, str]
1
  • I’m just unsure of what I would put in the ?? regardless of the typechecker Commented Aug 4, 2022 at 0:51

1 Answer 1

5
+50

I don't see any way to do this with the current spec. The main issue I see is that TypeVarTuple does not support bounds. You can't constrain the types referred to by Ts to be bounded to S.

You need to translate somehow tuple[S[T1], S[T2], ...] -> tuple[T1, T2, ...], but you have no way to know that the types contained by Ts are specializations of S or that types themselves are generic with further parameterization.


Without using TypeVarTuple, your goal can be accomplished to some extent with a pattern like the following, using overload to handle subsets of the signature for differing amounts of arguments. I also use an ending / in the overloads to prevent usage of named arguments (forcing positional args to be used), which allows the overloads to match the real method definition.

Obviously, this pattern becomes awkward as you add more ranges of arguments, but in some cases it can be a nice escape hatch.

from dataclasses import dataclass
from typing import Any, Generic, TypeVar, assert_type, overload

T = TypeVar("T")

@dataclass
class S(Generic[T]):
    data: T
...

T1 = TypeVar("T1")
T2 = TypeVar("T2")
T3 = TypeVar("T3")

@overload
def data_from_s(s1: S[T1], /) -> tuple[T1]:
    ...

@overload
def data_from_s(s1: S[T1], s2: S[T2], /) -> tuple[T1, T2]:
    ...

@overload
def data_from_s(s1: S[T1], s2: S[T2], s3: S[T3], /) -> tuple[T1, T2, T3]:
    ...

def data_from_s(*structs: S[Any]) -> tuple[Any, ...]:
    return tuple(x.data for x in structs)

Which will pass this test:

assert_type(
    data_from_s(S(1)),
    tuple[int]
)
assert_type(
    data_from_s(S(1), S("3")), 
    tuple[int, str]
)
assert_type(
    data_from_s(S(1), S("3"), S(3.9)), 
    tuple[int, str, float]
)
Sign up to request clarification or add additional context in comments.

1 Comment

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.