[ty] fix unpacking a type alias with detailed tuple spec (#19981)

## Summary

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

We special-case iteration of certain types because they may have a more
detailed tuple-spec. Now that type aliases are a distinct type variant,
we need to handle them as well.

I don't love that `Type::TypeAlias` means we have to remember to add a
case for it basically anywhere we are special-casing a certain kind of
type, but at the moment I don't have a better plan. It's another
argument for avoiding fallback cases in `Type` matches, which we usually
prefer; I've updated this match statement to be comprehensive.

## Test Plan

Added mdtest.
This commit is contained in:
Carl Meyer 2025-08-18 17:54:05 -07:00 committed by GitHub
parent e6dcdd29f2
commit a04375173c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 44 additions and 1 deletions

View file

@ -64,6 +64,17 @@ x: MyIntOrStr = 1
y: MyIntOrStr = None
```
## Unpacking from a type alias
```py
type T = tuple[int, str]
def f(x: T):
a, b = x
reveal_type(a) # revealed: int
reveal_type(b) # revealed: str
```
## Generic type aliases
```py

View file

@ -4866,7 +4866,39 @@ impl<'db> Type<'db> {
// diagnostic in unreachable code.
return Ok(Cow::Owned(TupleSpec::homogeneous(Type::unknown())));
}
_ => {}
Type::TypeAlias(alias) => {
return alias.value_type(db).try_iterate_with_mode(db, mode);
}
Type::Dynamic(_)
| Type::FunctionLiteral(_)
| Type::GenericAlias(_)
| Type::BoundMethod(_)
| Type::MethodWrapper(_)
| Type::WrapperDescriptor(_)
| Type::DataclassDecorator(_)
| Type::DataclassTransformer(_)
| Type::Callable(_)
| Type::ModuleLiteral(_)
| Type::ClassLiteral(_)
| Type::SubclassOf(_)
| Type::ProtocolInstance(_)
| Type::SpecialForm(_)
| Type::KnownInstance(_)
| Type::PropertyInstance(_)
| Type::Union(_)
| Type::Intersection(_)
| Type::AlwaysTruthy
| Type::AlwaysFalsy
| Type::IntLiteral(_)
| Type::BooleanLiteral(_)
| Type::EnumLiteral(_)
| Type::LiteralString
| Type::BytesLiteral(_)
| Type::TypeVar(_)
| Type::NonInferableTypeVar(_)
| Type::BoundSuper(_)
| Type::TypeIs(_)
| Type::TypedDict(_) => {}
}
let try_call_dunder_getitem = || {