Skip to content

Commit bc6308b

Browse files
authored
Add docs for Literal types (#6150)
This pull request adds some documentation on using Literal types. It focuses mostly on how they can be parameterized, and different ways of declaring variables to be Literal. These docs deliberately do not mention the "intelligent indexing" feature: we don't yet support intelligently indexing using variables that are declared to be Final, so I think we should leave this feature undocumented for now.
1 parent 8179952 commit bc6308b

File tree

4 files changed

+164
-2
lines changed

4 files changed

+164
-2
lines changed

docs/source/final_attrs.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
.. _final_attrs:
2+
13
Final names, methods and classes
24
================================
35

docs/source/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ Mypy is a static type checker for Python 3 and Python 2.7.
3939
stubs
4040
generics
4141
more_types
42+
literal_types
4243
final_attrs
4344
metaclasses
4445

docs/source/literal_types.rst

Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
.. _literal_types:
2+
3+
Literal types
4+
=============
5+
6+
.. note::
7+
8+
Literal is an officially supported feature, but is highly experimental
9+
and should be considered to be in alpha stage. It is very likely that future
10+
releases of mypy will modify the behavior of literal types, either by adding
11+
new features or by tuning or removing problematic ones.
12+
13+
Literal types let you indicate that an expression is equal to some specific
14+
primitive value. For example, if we annotate a variable with type ``Literal["foo"]``,
15+
mypy will understand that variable is not only of type ``str``, but is also
16+
equal to specifically the string ``"foo"``.
17+
18+
This feature is primarily useful when annotating functions that behave
19+
differently based on the exact value the caller provides. For example,
20+
suppose we have a function ``fetch_data(...)`` that returns ``bytes`` if the
21+
first argument is ``True``, and ``str`` if it's ``False``. We can construct a
22+
precise type signature for this function using ``Literal[...]`` and overloads:
23+
24+
.. code-block:: python
25+
26+
from typing import overload, Union
27+
from typing_extensions import Literal
28+
29+
# The first two overloads use Literal[...] so we can
30+
# have precise return types:
31+
32+
@overload
33+
def fetch_data(raw: Literal[True]) -> bytes: ...
34+
@overload
35+
def fetch_data(raw: Literal[False]) -> str: ...
36+
37+
# The last overload is a fallback in case the caller
38+
# provides a regular bool:
39+
40+
@overload
41+
def fetch_data(raw: bool) -> Union[bytes, str]: ...
42+
43+
def fetch_data(raw: bool) -> Union[bytes, str]:
44+
# Implementation is omitted
45+
...
46+
47+
reveal_type(fetch_data(True)) # Revealed type is 'bytes'
48+
reveal_type(fetch_data(False)) # Revealed type is 'str'
49+
50+
# Variables declared without annotations will continue to have an
51+
# inferred type of 'bool'.
52+
53+
variable = True
54+
reveal_type(fetch_data(variable)) # Revealed type is 'Union[bytes, str]'
55+
56+
Parameterizing Literals
57+
***********************
58+
59+
Literal types may contain one or more literal bools, ints, strs, and bytes.
60+
However, literal types **cannot** contain arbitrary expressions:
61+
types like ``Literal[my_string.trim()]``, ``Literal[x > 3]``, or ``Literal[3j + 4]``
62+
are all illegal.
63+
64+
Literals containing two or more values are equivalent to the union of those values.
65+
So, ``Literal[-3, b"foo", True]`` is equivalent to
66+
``Union[Literal[-3], Literal[b"foo"], Literal[True]]``. This makes writing
67+
more complex types involving literals a little more convenient.
68+
69+
Literal types may also contain ``None``. Mypy will treat ``Literal[None]`` as being
70+
equivalent to just ``None``. This means that ``Literal[4, None]``,
71+
``Union[Literal[4], None]``, and ``Optional[Literal[4]]`` are all equivalent.
72+
73+
Literals may also contain aliases to other literal types. For example, the
74+
following program is legal:
75+
76+
.. code-block:: python
77+
78+
PrimaryColors = Literal["red", "blue", "yellow"]
79+
SecondaryColors = Literal["purple", "green", "orange"]
80+
AllowedColors = Literal[PrimaryColors, SecondaryColors]
81+
82+
def paint(color: AllowedColors) -> None: ...
83+
84+
paint("red") # Type checks!
85+
paint("turquoise") # Does not type check
86+
87+
Literals may not contain any other kind of type or expression. This means doing
88+
``Literal[my_instance]``, ``Literal[Any]``, ``Literal[3.14]``, or
89+
``Literal[{"foo": 2, "bar": 5}]`` are all illegal.
90+
91+
Future versions of mypy may relax some of these restrictions. For example, we
92+
plan on adding support for using enum values inside ``Literal[...]`` in an upcoming release.
93+
94+
Declaring literal variables
95+
***************************
96+
97+
You must explicitly add an annotation to a variable to declare that it has
98+
a literal type:
99+
100+
.. code-block:: python
101+
102+
a: Literal[19] = 19
103+
reveal_type(a) # Revealed type is 'Literal[19]'
104+
105+
In order to preserve backwards-compatibility, variables without this annotation
106+
are **not** assumed to be literals:
107+
108+
.. code-block:: python
109+
110+
b = 19
111+
reveal_type(b) # Revealed type is 'int'
112+
113+
If you find repeating the value of the variable in the type hint to be tedious,
114+
you can instead change the variable to be :ref:`Final <final_attrs>`:
115+
116+
.. code-block:: python
117+
118+
from typing_extensions import Final, Literal
119+
120+
def expects_literal(x: Literal[19]) -> None: pass
121+
122+
c: Final = 19
123+
124+
reveal_type(c) # Revealed type is 'int'
125+
expects_literal(c) # ...but this type checks!
126+
127+
If you do not provide an explicit type in the Final, the type of ``c`` becomes
128+
context-sensitive: mypy will basically try "substituting" the original assigned
129+
value whenever it's used before performing type checking. So, mypy will type-check
130+
the above program almost as if it were written like so:
131+
132+
.. code-block:: python
133+
134+
from typing_extensions import Final, Literal
135+
136+
def expects_literal(x: Literal[19]) -> None: pass
137+
138+
reveal_type(19)
139+
expects_literal(19)
140+
141+
This is why ``expects_literal(19)`` type-checks despite the fact that ``reveal_type(c)``
142+
reports ``int``.
143+
144+
So while changing a variable to be Final is not quite the same thing as adding
145+
an explicit ``Literal[...]`` annotation, it often leads to the same effect in practice.
146+
147+
Limitations
148+
***********
149+
150+
Mypy will not understand expressions that use variables of type ``Literal[..]``
151+
on a deep level. For example, if you have a variable ``a`` of type ``Literal[3]``
152+
and another variable ``b`` of type ``Literal[5]``, mypy will infer that
153+
``a + b`` has type ``int``, **not** type ``Literal[8]``.
154+
155+
The basic rule is that literal types are treated as just regular subtypes of
156+
whatever type the parameter has. For example, ``Literal[3]`` is treated as a
157+
subtype of ``int`` and so will inherit all of ``int``'s methods directly. This
158+
means that ``Literal[3].__add__`` accepts the same arguments and has the same
159+
return type as ``int.__add__``.

docs/source/more_types.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ More types
22
==========
33

44
This section introduces a few additional kinds of types, including ``NoReturn``,
5-
``NewType``, ``TypedDict``, and types for async code. It also discusses how to
6-
give functions more precise types using overloads. All of these are only
5+
``NewType``, ``TypedDict``, and types for async code. It also discusses
6+
how to give functions more precise types using overloads. All of these are only
77
situationally useful, so feel free to skip this section and come back when you
88
have a need for some of them.
99

0 commit comments

Comments
 (0)