Skip to content

bpo-41923: PEP 613: Add TypeAlias to typing module #22532

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions Doc/library/typing.rst
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ In the function ``greeting``, the argument ``name`` is expected to be of type
:class:`str` and the return type :class:`str`. Subtypes are accepted as
arguments.

.. _type-aliases:

Type aliases
============

Expand Down Expand Up @@ -489,6 +491,17 @@ These can be used as types in annotations and do not support ``[]``.
.. versionadded:: 3.5.4
.. versionadded:: 3.6.2

.. data:: TypeAlias

Special annotation for explicitly declaring a :ref:`type alias <type-aliases>`.
For example::

from typing import TypeAlias

Factors: TypeAlias = list[int]

.. versionadded:: 3.10

Special forms
"""""""""""""

Expand Down
25 changes: 23 additions & 2 deletions Doc/whatsnew/3.10.rst
Original file line number Diff line number Diff line change
Expand Up @@ -82,8 +82,29 @@ New Features
* :pep:`618`: The :func:`zip` function now has an optional ``strict`` flag, used
to require that all the iterables have an equal length.

PEP604: New Type Operator
-------------------------
PEP 613: TypeAlias Annotation
-----------------------------

:pep:`484` introduced the concept of type aliases, only requiring them to be
top-level unannotated assignments. This simplicity sometimes made it difficult
for type checkers to distinguish between type aliases and ordinary assignments,
especially when forward references or invalid types were involved. Compare::

StrCache = 'Cache[str]' # a type alias
LOG_PREFIX = 'LOG[DEBUG]' # a module constant

Now the :mod:`typing` module has a special annotation :data:`TypeAlias` to
declare type aliases more explicitly::

StrCache: TypeAlias = 'Cache[str]' # a type alias
LOG_PREFIX = 'LOG[DEBUG]' # a module constant

See :pep:`613` for more details.

(Contributed by Mikhail Golubev in :issue:`41923`.)

PEP604: New Type Union Operator
-------------------------------

A new type union operator was introduced which enables the syntax ``X | Y``.
This provides a cleaner way of expressing 'either type X or type Y' instead of
Expand Down
40 changes: 40 additions & 0 deletions Lib/test/test_typing.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
from typing import IO, TextIO, BinaryIO
from typing import Pattern, Match
from typing import Annotated, ForwardRef
from typing import TypeAlias
import abc
import typing
import weakref
Expand Down Expand Up @@ -4176,6 +4177,45 @@ def test_annotated_in_other_types(self):
self.assertEqual(X[int], List[Annotated[int, 5]])


class TypeAliasTests(BaseTestCase):
def test_canonical_usage_with_variable_annotation(self):
Alias: TypeAlias = Employee

def test_canonical_usage_with_type_comment(self):
Alias = Employee # type: TypeAlias

def test_cannot_instantiate(self):
with self.assertRaises(TypeError):
TypeAlias()

def test_no_isinstance(self):
with self.assertRaises(TypeError):
isinstance(42, TypeAlias)

def test_no_issubclass(self):
with self.assertRaises(TypeError):
issubclass(Employee, TypeAlias)

with self.assertRaises(TypeError):
issubclass(TypeAlias, Employee)

def test_cannot_subclass(self):
with self.assertRaises(TypeError):
class C(TypeAlias):
pass

with self.assertRaises(TypeError):
class C(type(TypeAlias)):
pass

def test_repr(self):
self.assertEqual(repr(TypeAlias), 'typing.TypeAlias')

def test_cannot_subscript(self):
with self.assertRaises(TypeError):
TypeAlias[int]


class AllTests(BaseTestCase):
"""Tests for __all__."""

Expand Down
16 changes: 16 additions & 0 deletions Lib/typing.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@
'runtime_checkable',
'Text',
'TYPE_CHECKING',
'TypeAlias',
]

# The pseudo-submodules 're' and 'io' are part of the public
Expand Down Expand Up @@ -459,6 +460,21 @@ def open_helper(file: str, mode: MODE) -> str:
return _GenericAlias(self, parameters)


@_SpecialForm
def TypeAlias(self, parameters):
"""Special marker indicating that an assignment should
be recognized as a proper type alias definition by type
checkers.

For example::

Predicate: TypeAlias = Callable[..., bool]

It's invalid when used anywhere except as in the example above.
"""
raise TypeError(f"{self} is not subscriptable")


class ForwardRef(_Final, _root=True):
"""Internal wrapper to hold a forward reference."""

Expand Down
1 change: 1 addition & 0 deletions Misc/ACKS
Original file line number Diff line number Diff line change
Expand Up @@ -611,6 +611,7 @@ Christoph Gohlke
Tim Golden
Yonatan Goldschmidt
Mark Gollahon
Mikhail Golubev
Guilherme Gonçalves
Tiago Gonçalves
Chris Gonnerman
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Implement :pep:`613`, introducing :data:`typing.TypeAlias` annotation.