[ty] Support typing.TypeAliasType (#18156)

## Summary

Support direct uses of `typing.TypeAliasType`, as in:

```py
from typing import TypeAliasType

IntOrStr = TypeAliasType("IntOrStr", int | str)

def f(x: IntOrStr) -> None:
    reveal_type(x)  # revealed: int | str
```

closes https://github.com/astral-sh/ty/issues/392

## Ecosystem

The new false positive here:
```diff
+ error[invalid-type-form] altair/utils/core.py:49:53: The first argument to `Callable` must be either a list of types, ParamSpec, Concatenate, or `...`
```
comes from the fact that we infer the second argument as a type
expression now. We silence false positives for PEP695 `ParamSpec`s, but
not for `P = ParamSpec("P")` inside `Callable[P, ...]`.

## Test Plan

New Markdown tests
This commit is contained in:
David Peter 2025-05-19 16:36:49 +02:00 committed by GitHub
parent 220137ca7b
commit 4c889d5251
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 347 additions and 74 deletions

View file

@ -87,3 +87,54 @@ type TypeAliasType2 = TypeOf[Alias2]
static_assert(not is_equivalent_to(TypeAliasType1, TypeAliasType2))
static_assert(is_disjoint_from(TypeAliasType1, TypeAliasType2))
```
## Direct use of `TypeAliasType`
`TypeAliasType` can also be used directly. This is useful for versions of Python prior to 3.12.
```toml
[environment]
python-version = "3.9"
```
### Basic example
```py
from typing_extensions import TypeAliasType, Union
IntOrStr = TypeAliasType("IntOrStr", Union[int, str])
reveal_type(IntOrStr) # revealed: typing.TypeAliasType
reveal_type(IntOrStr.__name__) # revealed: Literal["IntOrStr"]
def f(x: IntOrStr) -> None:
reveal_type(x) # revealed: int | str
```
### Generic example
```py
from typing_extensions import TypeAliasType, TypeVar
T = TypeVar("T")
IntAnd = TypeAliasType("IntAndT", tuple[int, T], type_params=(T,))
def f(x: IntAnd[str]) -> None:
reveal_type(x) # revealed: @Todo(Generic PEP-695 type alias)
```
### Error cases
#### Name is not a string literal
```py
from typing_extensions import TypeAliasType
def get_name() -> str:
return "IntOrStr"
# error: [invalid-type-alias-type] "The name of a `typing.TypeAlias` must be a string literal"
IntOrStr = TypeAliasType(get_name(), int | str)
```