I made a PR to numpy to improve shape typing. The primary purpose was to make the type variable for shape (a) bound to a tuple of ints and (b) covariant.

As an additional feature, someone requested an alias that used `TypeVarTuple`

, as in the PEP 646 example. Although `TypeVarTuple`

cannot be bound or made covariant, unpacking one inside a bound typevar *seemed* to work, and still does, for the most part. What is the intended intended behavior? As a related question, is there any activity to add bounds/variance to `TypeVarTuple`

?

# MWE (as debug.pyi, to avoid “___ is unbound” errors):

## Define Array alias to ndarray (no numpy)

```
from typing import Generic, NewType, TypeVar
import sys
if sys.version_info < (3, 11):
from typing_extensions import assert_type, reveal_type, TypeVarTuple, Unpack
else:
from typing import assert_type, reveal_type, TypeVarTuple, Unpack
_ShapeType = TypeVar("_ShapeType", covariant=True, bound=tuple[int, ...])
_ShapeTypeTuple = TypeVarTuple("_ShapeTypeTuple")
class ndarray(Generic[_ShapeType]):
shape: _ShapeType
Array = ndarray[tuple[Unpack[_ShapeTypeTuple]]]
```

The definition of `Array`

causes a type error in both pyright and mypy: `Type argument "tuple[*_ShapeTypeTuple]" of "ndarray" must be a subtype of "tuple[int, ...]"`

. The crux here is whether type checkers still safely handle functions that use `Array`

, and whether that safety is guaranteed or an accident of implementation.

## Test 1: Array parameters unpacked correctly, bounds apply

```
Length = NewType("Length", int)
Width = NewType("Width", int)
arr: Array[Length, Width]
assert_type(arr.shape, tuple[Length, Width])
bad: Array[str]
```

This passes pyright. OTOH, mypy allows the definition of `arr`

, but correctly flags the definiton of `bad`

with `"tuple[str]" of "ndarray" must be a subtype of "tuple[int, ...]"`

. I assume this means that pyright has decided that since the `Array`

definition is flawed, it can’t provide any safety to `Array`

objects. Mypy, however, seems to pass the bounds of `_ShapeType`

through `_ShapeTypeTuple`

.

## Test 2: I can use Array in function signatures

```
Ts = TypeVarTuple("Ts")
def stack(a: Array[Unpack[Ts]], b: Array[Unpack[Ts]]) -> Array[int, Unpack[Ts]]: ...
doubled = stack(arr, arr)
assert_type(doubled.shape, tuple[int, Length, Width])
doublebad = stack(bad, bad)
```

Mypy raises an error about all the types in the `stack`

signature (`"tuple[*Ts]"/"tuple[int, *Ts]" must be a subtype of subtype of "tuple[int, ...]"`

), but pyright does not. I assume that’s because of when each resolves type aliases. Both pass the `assert_type`

, but both unfortunately also accept `doubelbad`

. Nevertheless, if mypy prevents us from ever creating an `Array[str]`

, it would seem like a safe to ignore the function signature errors, no?

## Corollary problem: Cannot unpack tuple-bound `TypeVar`

```
T = TypeVar("T", covariant=True, bound=tuple[int, ...])
def stack2(a: ndarray[T], b: ndarray[T]) -> ndarray[tuple[int, Unpack[T]]]: ...
doubled2 = stack2(arr, arr)
assert_type(doubled2.shape, tuple[int, Length, Width])
doublebad2 = stack2(bad, bad)
```

Here, both mypy and pyright complain that they can only unpack a `tuple`

or a `TypeVarTuple`

, but not a `tuple`

-bound `TypeVar`

. As a result, the `assert_type`

fails, as since the shape of `doubled2`

is either `tuple[int, *tuple[Any, ...]]`

(mypy) or `tuple[int, Unknown]`

(pyright). This is unfortunate, because this definition (correctly) causes type checkers to complain about `stack2(bad, bad)`