# Proposal: Allow Typevartuple unpacking in unions

**URL:** <https://discuss.python.org/t/proposal-allow-typevartuple-unpacking-in-unions/42932>\
**Category:** Typing\
**Created:** [January 11, 2024, 3:30am UTC](https://discuss.python.org/t/proposal-allow-typevartuple-unpacking-in-unions/42932 "2024-01-11T03:30:30Z")\
**Posts on this page:** 5\
**Page:** 1

<div class="post-metadata">

**Author:** ![mikeshardmind](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/mikeshardmind/32/14381_2.png) [@mikeshardmind](https://discuss.python.org/u/mikeshardmind)\
**Post date:** [January 11, 2024, 3:30am UTC](https://discuss.python.org/t/proposal-allow-typevartuple-unpacking-in-unions/42932/1 "2024-01-11T03:30:30Z")

</div>

Various uses of this, including 2 which showed up in Pyright’s change to move support for this behind a flag for experimental features have emerged. Support for this was originally part of pep 646, but was removed as part of the last edit made to it, which was done _after_ it was accepted (With the removals noted in the acceptance)

- `Union[*Ts]` should be a supported construct so that typevar tuples behave identically across places where type vars are accepted.
- `Union` should not be considered deprecated, only considered as not what people should reach for first.
- This also resolves a situation where even basic things like Iterating over a generic user-defined heterogeneous container can’t have the type of the iterator be typed properly.

Followup from, and some surrounding context [here](https://discuss.python.org/t/a-more-useful-and-less-divisive-future-for-typing/34225/99), there has also been discussion of this not existing as negatively impacting people in the typing channel of the python discord.

An Implementation for this already exists in Pyright, but is currently behind a flag, and is slated for potential future removal if this remains not part of the spec.

---

<div class="post-metadata">

**Author:** ![randolf-scholz](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/randolf-scholz/32/11123_2.png) [@randolf-scholz](https://discuss.python.org/u/randolf-scholz)\
**Post date:** [January 11, 2024, 9:41pm UTC](https://discuss.python.org/t/proposal-allow-typevartuple-unpacking-in-unions/42932/2 "2024-01-11T21:41:23Z")

</div>

An example of a function that requires `Union[*Ts]` is a random choice between multiple items of potentially different types:

```python
import random
from typing import Union, Never, assert_type

def rng[*Ts](*elements: *Ts) -> Union[*Ts]:
    return random.choice(elements)

x: bool = True
y: int = 1
z: str = "1"

assert_type(rng(x, y, z), bool | int | str)
assert_type(rng(x, y), bool | int)
assert_type(rng(x), bool)
assert_type(rng(), Never)

```

---

<div class="post-metadata">

**Author:** ![williamw4096](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/williamw4096/32/17808_2.png) [@williamw4096](https://discuss.python.org/u/williamw4096)\
**Post date:** [February 12, 2024, 2:30pm UTC](https://discuss.python.org/t/proposal-allow-typevartuple-unpacking-in-unions/42932/3 "2024-02-12T14:30:28Z")

</div>

Absolutely agree with this, however at least in the meantime, or if this proposal is rejected, it is possible to work around this (at least if using Pyright). Using regular type variables with different types in Pyright makes the inferred type of the variable the union of all the types it was used for (which is not true in mypy, where the behaviour generally seems to be to find the last common ancestor but is inconsistent). This means many of these pick-one-from-mixed-types situations can use normal type variables, e.g. for the example above:

```python
def rng[T](*elements: T) -> T:
    return random.choice(elements)

```

has the desired behaviour. It is even possible to implement this in classes which use a TypeVarTuple, although it’s extremely ugly:

```python
from __future__ import annotations
from typing import Self

class ExampleIterator[*Ts]:
    def __init__ (self, given: tuple[*Ts]) -> None:
        self.given = given
        self.it = iter(given)
    
    def __iter__ (self) -> Self:
        return self

    def __next__ [T](self: ExampleIterator[*tuple[T, ...]]) -> T:
        return next(self.it)

```

Pyright will now correctly infer the equivalent of Union[\*Ts]. Of course this is very verbose and unclear compared to the desired syntax

---

<div class="post-metadata">

**Author:** ![sobolevn](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/sobolevn/32/9274_2.png) [@sobolevn](https://discuss.python.org/u/sobolevn)\
**Post date:** [March 30, 2025, 8:16pm UTC](https://discuss.python.org/t/proposal-allow-typevartuple-unpacking-in-unions/42932/4 "2025-03-30T20:16:24Z")

</div>

I think that this feature is totally needed. Today I tried this code:

```python
class Seq[*Ts]:
     def __init__ (self, *args: *Ts) -> None:
        self.args = args
     def nth(self, index: int) -> Union[*Ts]:
        return self.args[index]

```

just to find out that it is not supported.

This looks like a good candidate for a PEP 🙂

---

<div class="post-metadata">

**Author:** ![Eneg](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/eneg/32/18177_2.png) [@Eneg](https://discuss.python.org/u/Eneg)\
**Post date:** [April 1, 2025, 6:33am UTC](https://discuss.python.org/t/proposal-allow-typevartuple-unpacking-in-unions/42932/5 "2025-04-01T06:33:49Z")

</div>

If a PEP does emerge, perhaps it could also be expanded to address this:

> [@Parametrizing \*\*P with \[\*Ts\]](https://discuss.python.org/t/parametrizing-p-with-ts/82569):
>
> As per [PEP 646](https://peps.python.org/pep-0646/#type-variable-tuples-with-callable), Callable supports unpacking a TypeVarTuple in its parameter list to capture positional argument types. This functionality seems to be lost when we alias a callable using \*\*P: from collections import abc from typing import Any type CoroFunc[\*\*P, T] = abc.Callable[P, abc.Coroutine[Any, Any, T]] def accepts\_func[\*ArgsTs]( func: abc.Callable[[object, \*ArgsTs], abc.Coroutine[Any, Any, object]], func2: CoroFunc[[object, \*ArgsTs], object], # error: TypeVarTuple is not allow…
