Skip to content

Commit 4f3c250

Browse files
authored
bpo-41923: PEP 613: Add TypeAlias to typing module (#22532)
This special marker annotation is intended to help in distinguishing proper PEP 484-compliant type aliases from regular top-level variable assignments.
1 parent f90dc36 commit 4f3c250

File tree

6 files changed

+94
-2
lines changed

6 files changed

+94
-2
lines changed

Doc/library/typing.rst

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ In the function ``greeting``, the argument ``name`` is expected to be of type
3434
:class:`str` and the return type :class:`str`. Subtypes are accepted as
3535
arguments.
3636

37+
.. _type-aliases:
38+
3739
Type aliases
3840
============
3941

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

494+
.. data:: TypeAlias
495+
496+
Special annotation for explicitly declaring a :ref:`type alias <type-aliases>`.
497+
For example::
498+
499+
from typing import TypeAlias
500+
501+
Factors: TypeAlias = list[int]
502+
503+
.. versionadded:: 3.10
504+
492505
Special forms
493506
"""""""""""""
494507

Doc/whatsnew/3.10.rst

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -99,8 +99,29 @@ in :issue:`38605`.)
9999
* :pep:`618`: The :func:`zip` function now has an optional ``strict`` flag, used
100100
to require that all the iterables have an equal length.
101101

102-
PEP604: New Type Operator
103-
-------------------------
102+
PEP 613: TypeAlias Annotation
103+
-----------------------------
104+
105+
:pep:`484` introduced the concept of type aliases, only requiring them to be
106+
top-level unannotated assignments. This simplicity sometimes made it difficult
107+
for type checkers to distinguish between type aliases and ordinary assignments,
108+
especially when forward references or invalid types were involved. Compare::
109+
110+
StrCache = 'Cache[str]' # a type alias
111+
LOG_PREFIX = 'LOG[DEBUG]' # a module constant
112+
113+
Now the :mod:`typing` module has a special annotation :data:`TypeAlias` to
114+
declare type aliases more explicitly::
115+
116+
StrCache: TypeAlias = 'Cache[str]' # a type alias
117+
LOG_PREFIX = 'LOG[DEBUG]' # a module constant
118+
119+
See :pep:`613` for more details.
120+
121+
(Contributed by Mikhail Golubev in :issue:`41923`.)
122+
123+
PEP604: New Type Union Operator
124+
-------------------------------
104125

105126
A new type union operator was introduced which enables the syntax ``X | Y``.
106127
This provides a cleaner way of expressing 'either type X or type Y' instead of

Lib/test/test_typing.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
from typing import IO, TextIO, BinaryIO
2525
from typing import Pattern, Match
2626
from typing import Annotated, ForwardRef
27+
from typing import TypeAlias
2728
import abc
2829
import typing
2930
import weakref
@@ -4176,6 +4177,45 @@ def test_annotated_in_other_types(self):
41764177
self.assertEqual(X[int], List[Annotated[int, 5]])
41774178

41784179

4180+
class TypeAliasTests(BaseTestCase):
4181+
def test_canonical_usage_with_variable_annotation(self):
4182+
Alias: TypeAlias = Employee
4183+
4184+
def test_canonical_usage_with_type_comment(self):
4185+
Alias = Employee # type: TypeAlias
4186+
4187+
def test_cannot_instantiate(self):
4188+
with self.assertRaises(TypeError):
4189+
TypeAlias()
4190+
4191+
def test_no_isinstance(self):
4192+
with self.assertRaises(TypeError):
4193+
isinstance(42, TypeAlias)
4194+
4195+
def test_no_issubclass(self):
4196+
with self.assertRaises(TypeError):
4197+
issubclass(Employee, TypeAlias)
4198+
4199+
with self.assertRaises(TypeError):
4200+
issubclass(TypeAlias, Employee)
4201+
4202+
def test_cannot_subclass(self):
4203+
with self.assertRaises(TypeError):
4204+
class C(TypeAlias):
4205+
pass
4206+
4207+
with self.assertRaises(TypeError):
4208+
class C(type(TypeAlias)):
4209+
pass
4210+
4211+
def test_repr(self):
4212+
self.assertEqual(repr(TypeAlias), 'typing.TypeAlias')
4213+
4214+
def test_cannot_subscript(self):
4215+
with self.assertRaises(TypeError):
4216+
TypeAlias[int]
4217+
4218+
41794219
class AllTests(BaseTestCase):
41804220
"""Tests for __all__."""
41814221

Lib/typing.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@
113113
'runtime_checkable',
114114
'Text',
115115
'TYPE_CHECKING',
116+
'TypeAlias',
116117
]
117118

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

462463

464+
@_SpecialForm
465+
def TypeAlias(self, parameters):
466+
"""Special marker indicating that an assignment should
467+
be recognized as a proper type alias definition by type
468+
checkers.
469+
470+
For example::
471+
472+
Predicate: TypeAlias = Callable[..., bool]
473+
474+
It's invalid when used anywhere except as in the example above.
475+
"""
476+
raise TypeError(f"{self} is not subscriptable")
477+
478+
463479
class ForwardRef(_Final, _root=True):
464480
"""Internal wrapper to hold a forward reference."""
465481

Misc/ACKS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -611,6 +611,7 @@ Christoph Gohlke
611611
Tim Golden
612612
Yonatan Goldschmidt
613613
Mark Gollahon
614+
Mikhail Golubev
614615
Guilherme Gonçalves
615616
Tiago Gonçalves
616617
Chris Gonnerman
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Implement :pep:`613`, introducing :data:`typing.TypeAlias` annotation.

0 commit comments

Comments
 (0)