[ty] don't expand type aliases via type mappings unless necessary (#22241)

## Summary

`apply_type_mapping` always expands type aliases and operates on the
resulting types, which can lead to cluttered results due to excessive
type alias expansion in places where it is not actually needed.

Specifically, type aliases are expanded when displaying method
signatures, because we use `TypeMapping::BindSelf` to get the method
signature.

```python
type Scalar = int | float
type Array1d = list[Scalar] | tuple[Scalar]

def f(x: Scalar | Array1d) -> None: pass
reveal_type(f)  # revealed: def f(x: Scalar | Array1d) -> None

class Foo:
    def f(self, x: Scalar | Array1d) -> None: pass
# should be `bound method Foo.f(x: Scalar | Array1d) -> None`
reveal_type(Foo().f)  # revealed: bound method Foo.f(x: int | float | list[int | float] | tuple[int | float]) -> None
```

In this PR, when type mapping is performed on a type alias, the
expansion result without type mapping is compared with the expansion
result after type mapping, and if the two are equivalent, the expansion
is deemed redundant and canceled.

## Test Plan

mdtest updated
This commit is contained in:
Shunsuke Shibayama
2025-12-30 12:02:56 +09:00
committed by GitHub
parent 8716b4e230
commit c429ef8407
3 changed files with 79 additions and 3 deletions

View File

@@ -310,7 +310,7 @@ x11: list[Literal[1] | Literal[2] | Literal[3]] = [1, 2, 3]
reveal_type(x11) # revealed: list[Literal[1, 2, 3]]
x12: Y[Y[Literal[1]]] = [[1]]
reveal_type(x12) # revealed: list[list[Literal[1]]]
reveal_type(x12) # revealed: list[Y[Literal[1]]]
x13: list[tuple[Literal[1], Literal[2], Literal[3]]] = [(1, 2, 3)]
reveal_type(x13) # revealed: list[tuple[Literal[1], Literal[2], Literal[3]]]

View File

@@ -67,3 +67,53 @@ def _(x: object):
c = C(x)
reveal_type(c) # revealed: C[Top[(...)]]
```
## Type aliases are not expanded unless necessary
```toml
[environment]
python-version = "3.12"
```
```py
type Scalar = int | float
type Array1d = list[Scalar] | tuple[Scalar]
def f(x: Scalar | Array1d) -> None:
pass
reveal_type(f) # revealed: def f(x: Scalar | Array1d) -> None
class Foo:
def f(self, x: Scalar | Array1d) -> None:
pass
reveal_type(Foo().f) # revealed: bound method Foo.f(x: Scalar | Array1d) -> None
type ArrayNd = Scalar | list[ArrayNd] | tuple[ArrayNd]
def g(x: Scalar | ArrayNd) -> None:
pass
reveal_type(g) # revealed: def g(x: Scalar | ArrayNd) -> None
class Bar:
def g(self, x: Scalar | ArrayNd) -> None:
pass
# TODO: should be `bound method Bar.g(x: Scalar | ArrayNd) -> None`
reveal_type(Bar().g) # revealed: bound method Bar.g(x: Scalar | list[Any] | tuple[Any]) -> None
type GenericArray1d[T] = list[T] | tuple[T]
def h(x: Scalar | GenericArray1d[Scalar]) -> None:
pass
reveal_type(h) # revealed: def h(x: Scalar | GenericArray1d[Scalar]) -> None
class Baz:
def h(self, x: Scalar | GenericArray1d[Scalar]) -> None:
pass
reveal_type(Baz().h) # revealed: bound method Baz.h(x: Scalar | GenericArray1d[Scalar]) -> None
```