[ty] add support for mapped union and intersection subscript loads (#18846)

## Summary

Note this modifies the diagnostics a bit. Previously performing
subscript access on something like `NotSubscriptable1 |
NotSubscriptable2` would report the full type as not being
subscriptable:

```
[non-subscriptable] "Cannot subscript object of type `NotSubscriptable1 | NotSubscriptable2` with no `__getitem__` method"
```

Now each erroneous constituent has a separate error:

```
[non-subscriptable] "Cannot subscript object of type `NotSubscriptable2` with no `__getitem__` method"
[non-subscriptable] "Cannot subscript object of type `NotSubscriptable1` with no `__getitem__` method"
```

Closes https://github.com/astral-sh/ty/issues/625

## Test Plan

 mdtest

---------

Co-authored-by: Carl Meyer <carl@astral.sh>
This commit is contained in:
Suneet Tipirneni
2025-06-23 12:38:01 -04:00
committed by GitHub
parent a77db3da3f
commit ef8281b695
5 changed files with 54 additions and 24 deletions

View File

@@ -188,3 +188,30 @@ class C(Tuple): ...
# revealed: tuple[<class 'C'>, <class 'tuple[Unknown, ...]'>, <class 'Sequence[Unknown]'>, <class 'Reversible[Unknown]'>, <class 'Collection[Unknown]'>, <class 'Iterable[Unknown]'>, <class 'Container[Unknown]'>, typing.Protocol, typing.Generic, <class 'object'>]
reveal_type(C.__mro__)
```
### Union subscript access
```py
def test(val: tuple[str] | tuple[int]):
reveal_type(val[0]) # revealed: str | int
def test2(val: tuple[str, None] | list[int | float]):
reveal_type(val[0]) # revealed: str | int | float
```
### Union subscript access with non-indexable type
```py
def test3(val: tuple[str] | tuple[int] | int):
# error: [non-subscriptable] "Cannot subscript object of type `int` with no `__getitem__` method"
reveal_type(val[0]) # revealed: str | int | Unknown
```
### Intersection subscript access
```py
from ty_extensions import Intersection, Not
def test4(val: Intersection[tuple[str], tuple[int]]):
reveal_type(val[0]) # revealed: str & int
```