The typing module was introduced in Python 3.5 (Preliminary typing.py, anticipating provisional acceptance of PEP 484. · python/cpython@46dbb7d · GitHub). Initially, almost all these generic subscriptable things (List, Sequence, Callable) were classes (with special metaclass). The only exceptions were Union and Optional. Subscripting all of them returned an instance of the same class, so it was not easy to distinguish generic things from non-generic. There was a lot of hacking here.
In Python 3.7 it was re-designed (Implement PEP 560: Core support for typing module and generic types · Issue #76407 · python/cpython · GitHub) – now most of subscriptable things (except Generic) no longer classes, they were instances of _GenericAlias or _SpecialForm. Subscribing them returned an instance of _GenericAlias. Still a lot of hacking.
In Python 3.9 I refactored the whole hierarhy in more strictly typed way (Refactor typing._GenericAlias · Issue #84577 · python/cpython · GitHub), so List and List[int] became instances of different types. Subscribing any of subscrptable things now returns and instance of _GenericAlias or its more specialized subclasses (like _UnionGenericAlias). types.GenericAlias also was introduced in Python 3.9 (Implement PEP 585 (Type Hinting Generics In Standard Collections) · Issue #83662 · python/cpython · GitHub). It was similar to typing._GenericAlias and named alike. typling.List[int] returns typing._GenericAlias, list[int] returns types.GenericAlias.
In Python 3.10, types.UnionType together with type.__or__() was introduced (PEP 604 -- Allow writing union types as X | Y · Issue #85600 · python/cpython · GitHub). It was initially named types.Union, but was renamed in Rename types.Union to types.UnionType · Issue #88895 · python/cpython · GitHub to avoid confusion with typing.Union. It was similar to typing._UnionGenericAlias. typling.Union[int, str] returns typing._UnionGenericAlias, int | str returns types.UnionType.
It was planned to merge types.GenericAlias with typing._GenericAlias and types.UnionType with typing._UnionGenericAlias in distant future (after implementing the missing pieces in types.GenericAlias and types.UnionType).
types.UnionType has been merged during the 3.14 developing cycle, not with typing._UnionGenericAlias, but with typing.Union, which was not even a class. This breaks long planes for types.GenericAlias and types.UnionType and violates some principles.
typing.Unionnever was a class, from the beginning. Even when other generic types were classes. And there were reasons to make other generic types non-classes.types.UnionTypeshould not be subscriptable. It is not generic class, and classes should only be subscriptable to make a specialized type from generic class. The result of subscripting a class is alwaystypes.GenericAlias, nottypes.UnionType.
I suggest two options:
- Either completely revert Merge `typing.Union` and `types.UnionType` · Issue #105499 · python/cpython · GitHub. This is preferable, because
types.UnionTypeshould be merged withtyping._UnionGenericAliassimultaneously or later thantypes.GenericAliaswithtyping._GenericAlias. - Or revert it partially, by merging
types.UnionTypewith the right classtyping._UnionGenericAliasinstead oftyping.Union(gh-137065: Unmerge types.UnionType and typing.Union by serhiy-storchaka · Pull Request #137069 · python/cpython · GitHub).
I am sorry that I only found out about this change three weeks ago and we need to make a decision so late. But if we do not make it, I suspect that we will need to spent next few years undoing this change. It will be much more painfully.