[ty] support `type[tuple[...]]` (#21652)

Fixes https://github.com/astral-sh/ty/issues/1649

## Summary

We missed this when adding support for `type[]` of a specialized
generic.

## Test Plan

Added mdtests.
This commit is contained in:
Carl Meyer 2025-12-01 02:49:26 -08:00 committed by GitHub
parent bc6517a807
commit c2773b4c6f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 19 additions and 3 deletions

View File

@ -329,6 +329,16 @@ reveal_type(tuple[int, str]) # revealed: <class 'tuple[int, str]'>
reveal_type(tuple[int, ...]) # revealed: <class 'tuple[int, ...]'> reveal_type(tuple[int, ...]) # revealed: <class 'tuple[int, ...]'>
``` ```
```py
from typing import Any
def _(a: type[tuple], b: type[tuple[int]], c: type[tuple[int, ...]], d: type[tuple[Any, ...]]) -> None:
reveal_type(a) # revealed: type[tuple[Unknown, ...]]
reveal_type(b) # revealed: type[tuple[int]]
reveal_type(c) # revealed: type[tuple[int, ...]]
reveal_type(d) # revealed: type[tuple[Any, ...]]
```
## Inheritance ## Inheritance
```toml ```toml
@ -392,7 +402,7 @@ class C(Tuple): ...
reveal_mro(C) reveal_mro(C)
``` ```
### Union subscript access ## Union subscript access
```py ```py
def test(val: tuple[str] | tuple[int]): def test(val: tuple[str] | tuple[int]):
@ -402,7 +412,7 @@ def test2(val: tuple[str, None] | list[int | float]):
reveal_type(val[0]) # revealed: str | int | float reveal_type(val[0]) # revealed: str | int | float
``` ```
### Union subscript access with non-indexable type ## Union subscript access with non-indexable type
```py ```py
def test3(val: tuple[str] | tuple[int] | int): def test3(val: tuple[str] | tuple[int] | int):
@ -410,7 +420,7 @@ def test3(val: tuple[str] | tuple[int] | int):
reveal_type(val[0]) # revealed: str | int | Unknown reveal_type(val[0]) # revealed: str | int | Unknown
``` ```
### Intersection subscript access ## Intersection subscript access
```py ```py
from ty_extensions import Intersection from ty_extensions import Intersection

View File

@ -691,6 +691,12 @@ impl<'db> TypeInferenceBuilder<'db, '_> {
self.db(), self.db(),
todo_type!("type[T] for protocols").expect_dynamic(), todo_type!("type[T] for protocols").expect_dynamic(),
) )
} else if class_literal.is_tuple(self.db()) {
let class_type = self
.infer_tuple_type_expression(parameters)
.map(|tuple_type| tuple_type.to_class_type(self.db()))
.unwrap_or_else(|| class_literal.default_specialization(self.db()));
SubclassOfType::from(self.db(), class_type)
} else { } else {
match class_literal.generic_context(self.db()) { match class_literal.generic_context(self.db()) {
Some(generic_context) => { Some(generic_context) => {