[ty] ensure union normalization really normalizes (#20147)

## Summary

Now that we have `Type::TypeAlias`, which can wrap a union, and the
possibility of unions including non-unpacked type aliases (which is
necessary to support recursive type aliases), we can no longer assume in
`UnionType::normalized_impl` that normalizing each element of an
existing union will result in a set of elements that we can order and
then place raw into `UnionType` to create a normalized union. It's now
possible for those elements to themselves include union types (unpacked
from an alias). So instead, we need to feed those elements into the full
`UnionBuilder` (with alias-unpacking turned on) to flatten/normalize
them, and then order them.

## Test Plan

Added mdtest.

---------

Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
This commit is contained in:
Carl Meyer
2025-08-29 09:02:35 -07:00
committed by GitHub
parent 5a608f7366
commit 8223fea062
3 changed files with 58 additions and 15 deletions

View File

@@ -120,6 +120,23 @@ def f(x: IntOrStr, y: str | bytes):
reveal_type(z) # revealed: (int & ~AlwaysFalsy) | str | bytes
```
## Multiple layers of union aliases
```py
class A: ...
class B: ...
class C: ...
class D: ...
type W = A | B
type X = C | D
type Y = W | X
from ty_extensions import is_equivalent_to, static_assert
static_assert(is_equivalent_to(Y, A | B | C | D))
```
## `TypeAliasType` properties
Two `TypeAliasType`s are distinct and disjoint, even if they refer to the same type