2020-01-22 21:13:06 -05:00
|
|
|
|
PEP: 613
|
|
|
|
|
Title: Explicit Type Aliases
|
|
|
|
|
Author: Shannon Zhu <szhu@fb.com>
|
|
|
|
|
Sponsor: Guido van Rossum <guido@python.org>
|
|
|
|
|
Discussions-To: https://mail.python.org/archives/list/typing-sig@python.org/thread/MWRJOBEEEMFVXE7CAKO7B4P46IPM4AN3/
|
2024-02-16 01:28:04 -05:00
|
|
|
|
Status: Final
|
2020-01-23 20:20:11 -05:00
|
|
|
|
Type: Standards Track
|
2022-10-06 20:36:39 -04:00
|
|
|
|
Topic: Typing
|
2020-01-22 21:13:06 -05:00
|
|
|
|
Created: 21-Jan-2020
|
2022-07-28 11:55:06 -04:00
|
|
|
|
Python-Version: 3.10
|
2020-01-22 21:13:06 -05:00
|
|
|
|
Post-History: 21-Jan-2020
|
|
|
|
|
|
2024-06-11 18:12:09 -04:00
|
|
|
|
.. canonical-typing-spec:: :ref:`typing:type-aliases` and
|
|
|
|
|
:py:data:`typing.TypeAlias`
|
2020-01-22 21:13:06 -05:00
|
|
|
|
|
|
|
|
|
Abstract
|
|
|
|
|
========
|
|
|
|
|
|
|
|
|
|
Type aliases are user-specified types which may be as complex as any type hint,
|
|
|
|
|
and are specified with a simple variable assignment on a module top level.
|
|
|
|
|
|
|
|
|
|
This PEP formalizes a way to explicitly declare an assignment as a type alias.
|
|
|
|
|
|
|
|
|
|
Motivation
|
|
|
|
|
==========
|
|
|
|
|
|
|
|
|
|
Type aliases are declared as top level variable assignments.
|
2022-01-21 06:03:51 -05:00
|
|
|
|
In :pep:`PEP 484 <484#type-aliases>`,
|
2020-01-22 21:13:06 -05:00
|
|
|
|
the distinction between a valid type alias and a global variable was implicitly
|
|
|
|
|
determined: if a top level assignment is unannotated, and the assigned value is
|
|
|
|
|
a valid type, then the name being assigned to is a valid type alias. Otherwise,
|
|
|
|
|
that name is simply a global value that cannot be used as a type hint.
|
|
|
|
|
|
|
|
|
|
These implicit type alias declaration rules create confusion when type aliases
|
|
|
|
|
involve forward references, invalid types, or violate other restrictions
|
|
|
|
|
enforced on type alias declaration. Because the distinction between an
|
|
|
|
|
unannotated value and a type alias is implicit, ambiguous or incorrect type
|
|
|
|
|
alias declarations implicitly default to a valid value assignment. This creates
|
|
|
|
|
expressions that are impossible to express as type aliases and punts error
|
|
|
|
|
diagnosis of malformed type aliases downstream.
|
|
|
|
|
|
|
|
|
|
The following examples each include an illustration of some of the suboptimal
|
|
|
|
|
or confusing behaviors resulting from existing implicit alias declarations.
|
2020-08-07 12:35:20 -04:00
|
|
|
|
We also introduce explicit aliases of the format ``TypeName: TypeAlias = Expression``
|
2020-01-22 21:13:06 -05:00
|
|
|
|
here for the sake of comparison, but the syntax is discussed in further detail
|
|
|
|
|
in later sections.
|
|
|
|
|
|
|
|
|
|
Forward References:
|
|
|
|
|
*******************
|
|
|
|
|
|
|
|
|
|
::
|
|
|
|
|
|
|
|
|
|
MyType = "ClassName"
|
|
|
|
|
def foo() -> MyType: ...
|
|
|
|
|
|
2020-08-07 12:35:20 -04:00
|
|
|
|
This code snippet should not error so long as ``ClassName`` is defined
|
2020-01-28 15:58:47 -05:00
|
|
|
|
later on. However, a type checker is forced to assume that MyType is a value
|
|
|
|
|
assignment rather than a type alias, and therefore may throw spurious errors
|
2020-08-07 12:35:20 -04:00
|
|
|
|
that (1) ``MyType`` is an unannotated global string, and (2) ``MyType``
|
2020-01-28 15:58:47 -05:00
|
|
|
|
cannot be used as a return annotation because it is not a valid type.
|
2020-01-22 21:13:06 -05:00
|
|
|
|
|
|
|
|
|
::
|
|
|
|
|
|
2021-12-09 08:37:35 -05:00
|
|
|
|
MyType: TypeAlias = "ClassName"
|
2020-01-22 21:13:06 -05:00
|
|
|
|
def foo() -> MyType: ...
|
|
|
|
|
|
|
|
|
|
Explicit aliases remove ambiguity so neither of the above errors will be
|
2020-08-07 12:35:20 -04:00
|
|
|
|
thrown. Additionally, if something is wrong with ``ClassName``
|
2020-01-22 21:13:06 -05:00
|
|
|
|
(i.e., it’s not actually defined later), the type checker can throw an error.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Error Messaging:
|
|
|
|
|
****************
|
|
|
|
|
|
|
|
|
|
::
|
|
|
|
|
|
|
|
|
|
MyType1 = InvalidType
|
|
|
|
|
MyType2 = MyGeneric(int) # i.e., intention was MyGeneric[int]
|
|
|
|
|
|
2020-08-07 12:35:20 -04:00
|
|
|
|
A type checker should warn on this code snippet that ``InvalidType`` is not
|
2020-01-22 21:13:06 -05:00
|
|
|
|
a valid type, and therefore cannot be used to annotate an expression or to
|
|
|
|
|
construct a type alias. Instead, type checkers are forced to throw spurious
|
2020-08-07 12:35:20 -04:00
|
|
|
|
errors that (1) ``MyType`` is a global expression missing an annotation,
|
|
|
|
|
and (2) ``MyType`` is not a valid type in all usages of ``MyType``
|
2020-01-22 21:13:06 -05:00
|
|
|
|
across the codebase.
|
|
|
|
|
|
|
|
|
|
::
|
|
|
|
|
|
|
|
|
|
MyType1: TypeAlias = InvalidType
|
|
|
|
|
MyType2: TypeAlias = MyGeneric(int)
|
|
|
|
|
|
|
|
|
|
With explicit aliases, the type checker has enough information to error on the
|
2020-08-07 12:35:20 -04:00
|
|
|
|
actual definition of the bad type alias, and explain why: that ``MyGeneric(int)``
|
|
|
|
|
and ``InvalidType`` are not valid types. When the value expression is no longer
|
|
|
|
|
evaluated as a global value, unactionable type errors on all usages of ``MyType``
|
2020-01-22 21:13:06 -05:00
|
|
|
|
across the codebase can be suppressed.
|
|
|
|
|
|
|
|
|
|
Scope Restrictions:
|
|
|
|
|
*******************
|
|
|
|
|
|
2022-01-12 16:28:17 -05:00
|
|
|
|
::
|
|
|
|
|
|
|
|
|
|
class Foo:
|
|
|
|
|
x = ClassName
|
|
|
|
|
y: TypeAlias = ClassName
|
|
|
|
|
z: Type[ClassName] = ClassName
|
|
|
|
|
|
|
|
|
|
Type aliases are valid within class scope, both implicitly (``x``) and
|
|
|
|
|
explicitly (``y``). If the line should be interpreted as a class
|
|
|
|
|
variable, it must be explicitly annotated (``z``).
|
|
|
|
|
|
2020-01-22 21:13:06 -05:00
|
|
|
|
::
|
|
|
|
|
|
|
|
|
|
x = ClassName
|
|
|
|
|
def foo() -> None:
|
|
|
|
|
x = ClassName
|
|
|
|
|
|
2020-08-07 12:35:20 -04:00
|
|
|
|
The outer ``x`` is a valid type alias, but type checkers must error if the
|
|
|
|
|
inner ``x`` is ever used as a type because type aliases cannot be defined
|
2022-01-12 16:28:17 -05:00
|
|
|
|
inside of a function.
|
2020-01-28 15:58:47 -05:00
|
|
|
|
This is confusing because the alias declaration rule is not explicit, and because
|
|
|
|
|
a type error will not be thrown on the location of the inner type alias declaration
|
|
|
|
|
but rather on every one of its subsequent use cases.
|
2020-01-22 21:13:06 -05:00
|
|
|
|
|
|
|
|
|
::
|
|
|
|
|
|
|
|
|
|
x: TypeAlias = ClassName
|
|
|
|
|
def foo() -> None:
|
|
|
|
|
x = ClassName
|
|
|
|
|
def bar() -> None:
|
|
|
|
|
x: TypeAlias = ClassName
|
|
|
|
|
|
2022-01-12 16:28:17 -05:00
|
|
|
|
With explicit aliases, the outer assignment is still a valid type variable.
|
|
|
|
|
Inside ``foo``, the inner assignment should be interpreted as ``x: Type[ClassName]``.
|
|
|
|
|
Inside ``bar``, the type checker should raise a clear error, communicating
|
|
|
|
|
to the author that type aliases cannot be defined inside a function.
|
2020-01-22 21:13:06 -05:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Specification
|
|
|
|
|
=============
|
|
|
|
|
|
|
|
|
|
The explicit alias declaration syntax clearly differentiates between the three
|
|
|
|
|
possible kinds of assignments: typed global expressions, untyped global
|
|
|
|
|
expressions, and type aliases. This avoids the existence of assignments that
|
|
|
|
|
break type checking when an annotation is added, and avoids classifying the
|
|
|
|
|
nature of the assignment based on the type of the value.
|
|
|
|
|
|
|
|
|
|
Implicit syntax (pre-existing):
|
|
|
|
|
|
|
|
|
|
::
|
|
|
|
|
|
|
|
|
|
x = 1 # untyped global expression
|
|
|
|
|
x: int = 1 # typed global expression
|
|
|
|
|
|
|
|
|
|
x = int # type alias
|
|
|
|
|
x: Type[int] = int # typed global expression
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Explicit syntax:
|
|
|
|
|
|
|
|
|
|
::
|
|
|
|
|
|
|
|
|
|
x = 1 # untyped global expression
|
|
|
|
|
x: int = 1 # typed global expression
|
|
|
|
|
|
|
|
|
|
x = int # untyped global expression (see note below)
|
|
|
|
|
x: Type[int] = int # typed global expression
|
|
|
|
|
|
|
|
|
|
x: TypeAlias = int # type alias
|
2021-12-09 08:37:35 -05:00
|
|
|
|
x: TypeAlias = "MyClass" # type alias
|
2020-01-22 21:13:06 -05:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Note: The examples above illustrate implicit and explicit alias declarations in
|
|
|
|
|
isolation. For the sake of backwards compatibility, type checkers should support
|
2020-08-07 12:35:20 -04:00
|
|
|
|
both simultaneously, meaning an untyped global expression ``x = int`` will
|
2020-01-22 21:13:06 -05:00
|
|
|
|
still be considered a valid type alias.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Backwards Compatibility
|
|
|
|
|
=======================
|
|
|
|
|
|
|
|
|
|
Explicit aliases provide an alternative way to declare type aliases, but all
|
|
|
|
|
pre-existing code and old alias declarations will work as before.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Reference Implementation
|
|
|
|
|
========================
|
|
|
|
|
|
|
|
|
|
The `Pyre <https://pyre-check.org/>`_ type checker supports explicit type
|
|
|
|
|
alias declarations.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Rejected Ideas
|
|
|
|
|
==============
|
|
|
|
|
|
|
|
|
|
Some alternative syntaxes were considered for explicit aliases:
|
|
|
|
|
|
|
|
|
|
::
|
|
|
|
|
|
|
|
|
|
MyType: TypeAlias[int]
|
|
|
|
|
|
|
|
|
|
This looks a lot like an uninitialized variable.
|
|
|
|
|
|
|
|
|
|
::
|
|
|
|
|
|
|
|
|
|
MyType = TypeAlias[int]
|
|
|
|
|
|
|
|
|
|
Along with the option above, this format potentially adds confusion around
|
2020-08-07 12:35:20 -04:00
|
|
|
|
what the runtime value of ``MyType`` is.
|
2020-01-22 21:13:06 -05:00
|
|
|
|
|
|
|
|
|
|
2020-08-07 12:35:20 -04:00
|
|
|
|
In comparison, the chosen syntax option ``MyType: TypeAlias = int`` is
|
|
|
|
|
appealing because it still sticks with the ``MyType = int`` assignment
|
2020-01-22 21:13:06 -05:00
|
|
|
|
syntax, and adds some information for the type checker purely as an annotation.
|
|
|
|
|
|
|
|
|
|
|
2022-01-12 16:28:17 -05:00
|
|
|
|
Version History
|
|
|
|
|
===============
|
|
|
|
|
|
|
|
|
|
* 2021-11-16
|
|
|
|
|
|
2024-02-16 01:28:04 -05:00
|
|
|
|
* Allow ``TypeAlias`` inside class scope
|
2022-01-12 16:28:17 -05:00
|
|
|
|
|
|
|
|
|
|
2020-01-22 21:13:06 -05:00
|
|
|
|
Copyright
|
|
|
|
|
=========
|
|
|
|
|
|
|
|
|
|
This document is placed in the public domain or under the
|
|
|
|
|
CC0-1.0-Universal license, whichever is more permissive.
|