diff --git a/pep-9999.rst b/pep-9999.rst index 29f03a2923d..6c2767025b4 100644 --- a/pep-9999.rst +++ b/pep-9999.rst @@ -1,6 +1,6 @@ PEP: 9999 Title: Literal types -Author: Some subset of the mypy team +Author: TODO Status: Draft Type: Standards Track Python-Version: 3.8 @@ -21,8 +21,8 @@ only expressions that have literally the value "4":: def accepts_only_four(x: Literal[4]) -> None: pass - accepts_only_four(4) # Type checker accepts - accepts_only_four(8) # Type checker rejects + accepts_only_four(4) # Ok + accepts_only_four(19) # Rejected **Note:** This PEP is still a very early draft: we plan on refining it as we add work on the corresponding implementation @@ -35,9 +35,9 @@ Python has many APIs that return different types depending on the value of some argument provided. For example: - ``open(filename, mode)`` returns either ``IO[bytes]`` or ``IO[Text]`` - depending on whether the second argument is something like “r” or - “rb” -- ``subprocess.check_output(…)`` returns either bytes or text + depending on whether the second argument is something like ``r`` or + ``rb``. +- ``subprocess.check_output(...)`` returns either bytes or text depending on whether the ``universal_newlines`` keyword argument is set to ``True`` or not. @@ -59,36 +59,33 @@ functions: PEP 484 does not include any mechanism for writing signatures where the return type varies depending on the value passed in. Note that this problem persists even if we redesign these APIs to instead accept enums: ``MyEnum.FOO`` and ``MyEnum.BAR`` are both -considered to be of type ``MyEnum``.) +considered to be of type ``MyEnum``. Currently, type checkers work around this limitation by adding ad-hoc extensions for important builtins and standard library functions. For example mypy comes bundled with a plugin that attempts to infer more -precise types for ``open(…)``. While this approach works for standard +precise types for ``open(...)``. While this approach works for standard library functions, it’s unsustainable in general: it’s not reasonable to expect 3rd party library authors to maintain plugins for N different type checkers, for example. We propose adding *Literal types* to address these gaps. -Specification -============= - Core Semantics --------------- +============== This section outlines the baseline behavior of literal types. Core behavior -''''''''''''' +------------- Literal types indicate a variable has a specific and concrete value. For example, if we define some variable ``foo`` to have type ``Literal[3]``, we are declaring that ``foo`` must be exactly equal to ``3`` and no other value. -Given some value ``V`` that is a member of type ``T``, the type -``Literal[V]`` shall be treated as a subtype of ``T``. For example, +Given some value ``v`` that is a member of type ``T``, the type +``Literal[v]`` shall be treated as a subtype of ``T``. For example, ``Literal[3]`` is a subtype of ``int``. All methods from the parent type will be directly inherited by the @@ -96,16 +93,17 @@ literal type. So, if we have some variable ``foo`` of type ``Literal[3]`` it’s safe to do things like ``foo + 5`` since ``foo`` inherits int’s ``__add__`` method. The resulting type of ``foo + 5`` is ``int``. -This “inheriting” behavior is identical to how we handle NewTypes. +This "inheriting" behavior is identical to how we +`handle NewTypes. `_. Equivalence of two Literals -''''''''''''''''''''''''''' +--------------------------- -Two types ``Literal[A]`` and ``Literal[B]`` are equivalent when +Two types ``Literal[v1]`` and ``Literal[v2]`` are equivalent when both of the following conditions are true: -1. ``type(A) == type(B)`` -2. ``A == B`` +1. ``type(v1) == type(v2)`` +2. ``v1 == v2`` For example, ``Literal[20]`` and ``Literal[0x14]`` are equivalent. However, ``Literal[0]`` and ``Literal[False]`` is *not* equivalent @@ -113,12 +111,12 @@ despite that ``0 == False`` evaluates to 'true' at runtime: ``0`` has type ``int`` and ``False`` has type ``bool``. Shortening unions of literals -''''''''''''''''''''''''''''' +----------------------------- Literals are parameterized with one or more value. When a Literal is -parameterized with more then one value, it's treated as exactly equivalent -to the union of those types. That is, ``Literal[V1, V2, V3]`` is equivalent -to ``Union[Literal[V1], Literal[V2], Literal[V3]]``. +parameterized with more than one value, it's treated as exactly equivalent +to the union of those types. That is, ``Literal[v1, v2, v3]`` is equivalent +to ``Union[Literal[v1], Literal[v2], Literal[v3]]``. This shortcut helps make writing signatures for functions that accept many different literals more ergonomic — for example, functions like @@ -140,69 +138,88 @@ many different literals more ergonomic — for example, functions like @overload def open(path: _PathType, mode: str) -> IO[Any]: ... -**Note:** Literals **must** be parameterized with at least one type. +The provided values do not all have to be members of the same type. +For example, ``Literal[42, "foo", True]`` is a legal type. + +However, Literal **must** be parameterized with at least one type. Types like ``Literal[]`` or ``Literal`` are illegal. + Legal and illegal parameterizations ------------------------------------ +=================================== + +This section describes what exactly constitutes a legal ``Literal[...]`` type: +what values may and may not be used as parameters. + +In short, a ``Literal[...]`` type may be parameterized by one or more literal expressions, +and nothing else. -This section describes exactly which values may or may not parameterize -a ``Literal[...]`` type. Legal parameters for ``Literal`` at type check time -''''''''''''''''''''''''''''''''''''''''''''''''''' +--------------------------------------------------- ``Literal`` may be parameterized with literal ints, native strings, -bools, Enum values, and ``None``. So for example, all of the following -would be legal:: +bools, Enum values and ``None``. So for example, all of +the following would be legal:: Literal[26] Literal[0x1A] # Exactly equivalent to Literal[26] + Literal[-4] Literal["hello world"] Literal[True] Literal[Color.RED] # Assuming Color is some enum Literal[None] -**Note:** The type ``Literal[None]`` is redundant in that the type -``None`` has only a single inhabitant. We nevertheless allow this -for consistency and ease-of-use. For example, when writing a literal -with multiple parameters, it might look a little cleaner to do -``Literal[1, 2, 3, None]`` instead of ``Optional[Literal[1, 2, 3]]``. +**Note:** Since the type ``None`` is inhabited by just a single +value, the types ``None`` and ``Literal[None]`` are exactly equivalent. +Type checkers may simplify ``Literal[None]`` into just ``None``. -Illegal parameters for ``Literal`` at type check time -''''''''''''''''''''''''''''''''''''''''''''''''''''' +``Literal`` may also be parameterized by other literal types, or type aliases +to other literal types. For example, the following is legal:: -The following are provisionally disallowed, mostly for -simplicity. We can consider adding these to the above list on a -case-by-case basis based on demand. + ReadOnlyMode = Literal["r", "r+"] + WriteAndTruncateMode = Literal["w", "w+", "wt", "w+t"] + WriteNoTruncateMode = Literal["r+", "r+t"] + AppendMode = Literal["a", "a+", "at", "a+t"] -- Explicit byte strings: e.g. ``Literal[b'foo']``. + AllModes = Literal[ReadOnlyMode, WriteAndTruncateMode, WriteNoTruncateMode, AppendMode] -- Explicit unicode strings: e.g. ``Literal[u'foo']``. +This feature is again intended to help make using and reusing literal types +more ergonomic. -- Floats: e.g. ``Literal[3.14]``. Note: if we do decide to allow - floats, we should likely disallow literal infinity and literal NaN. +**Note:** As a consequence of the above rules, type checkers are also expected +to support types that look like the following:: -- Any: e.g. ``Literal[Any]`` Note: the semantics of what exactly - ``Literal[Any]`` means would need to be clarified first. + Literal[Literal[Literal[1, 2, 3], "foo"], 5, 5, 5, None] + +This should be exactly equivalent to the following type:: -- Literal types themselves (or aliases to literal types). For example, if we - create a type alias ``BasicIds = Literal[1, 2, 3]``, then perhaps - ``Literal[100, BasicIds]`` should be treated as being equivalent to - ``Literal[100, 1, 2, 3]``. + Literal[1, 2, 3, "foo", 5, None] + +...and also to the following type:: + + Optional[Literal[1, 2, 3, "foo", 5]] + + +Illegal parameters for ``Literal`` at type check time +----------------------------------------------------- The following parameters are intentionally disallowed by design: -- Arbitrary expressions like ``Literal[3 + 4]`` or - ``Literal["foo".replace("o", "b")]``. Literal types are meant to be a - minimal extension to the PEP 484 typing ecosystem and requiring type - checkers to interpret potentially expressions inside types adds too - much complexity. Also see `Rejected or out-of-scope ideas`_. +- Arbitrary expressions like ``Literal[3 + 4]`` or + ``Literal["foo".replace("o", "b")]``. + + - Rationale: Literal types are meant to be a + minimal extension to the PEP 484 typing ecosystem and requiring type + checkers to interpret potentially expressions inside types adds too + much complexity. Also see `Rejected or out-of-scope ideas`_. + + - As a consequence, complex numbers like ``Literal[4 + 3j]`` and ``Literal[-4 + 2j]`` + are also prohibited. For consistency, literals like ``Literal[4j]`` that contain + just a single complex number are also prohibited. -- Complex numbers like ``Literal[4 + 3j]``, ``Literal[-4 + 2j]``, and - ``Literal[5j]``. Types like ``Literal[4 + 3j]`` would violate the - previous rule; it would then be consistent to also disallow types - like ``Literal[4j]``. + - The only exception to this rule is the unary ``-`` (minus) for ints: types + like ``Literal[-5]`` are *accepted*. - Tuples containing valid literal types like ``Literal[(1, "foo", "bar")]``. The user could always express this type as @@ -212,34 +229,48 @@ The following parameters are intentionally disallowed by design: - Mutable literal data structures like dict literals, list literals, or set literals: literals are always implicitly final and immutable. So, - ``Literal[{"a": "b", "c": "d"}]`` would be disallowed. + ``Literal[{"a": "b", "c": "d"}]`` is illegal. - Any other types: for example, ``Literal[MyTypedDict]``, or - ``Literal[some_object_instance]`` are disallowed. + ``Literal[some_object_instance]`` are illegal. This includes typevars: if ``T`` is a typevar, ``Literal[T]`` is not allowed. Typevars can vary over only types, never over values. +The following are provisionally disallowed for simplicity. We can consider allowing +them on a case-by-case basis based on demand. + +- Explicit byte strings: e.g. ``Literal[b'foo']``. + +- Explicit unicode strings: e.g. ``Literal[u'foo']``. + +- Floats: e.g. ``Literal[3.14]``. Note: if we do decide to allow + floats, we should likely disallow literal infinity and literal NaN. + +- Any: e.g. ``Literal[Any]`` Note: the semantics of what exactly + ``Literal[Any]`` means would need to be clarified first. + Parameters at runtime -''''''''''''''''''''' +--------------------- -The set of allowable parameters for ``Literal[...]`` is currently intentionally -very small. However, we may want to extend the ways in which we can use -``Literal[...]`` in the future. To help us retain this flexibility, the -actual implementation of ``typing.Literal`` will perform *no* checks on -any parameters provided at runtime. For example:: +Although the set of parameters ``Literal[...]`` may contain at type-check time +is very small, the actual implementation of ``typing.Literal`` will not perform +any checks at runtime. For example:: - def my_function(x: Literal[1 + 2]) -> None: - pass + def my_function(x: Literal[1 + 2]) -> int: + return x * 3 x: Literal = 3 - y: Literal[my_function] = my_funcion + y: Literal[my_function] = my_function The type checker should reject this program: all three uses of ``Literal`` are *invalid* according to this spec. However, Python itself should execute this program with no errors. +This helps us preserve flexibility in case we want to expand the scope of +what ``Literal`` can be used for in the future. + Literals, enums, and forward references -''''''''''''''''''''''''''''''''''''''' +--------------------------------------- One potential ambiguity is between literal strings and forward references to literal enum members. For example, suppose we have the @@ -249,73 +280,68 @@ enum member? In cases like these, we always assume the user meant to construct a literal string. If the user wants a forward reference, they must wrap -the entire literal type in a string -- e.g. ``Literal[Color.RED]``. - -The other alternative is to just not allow literal enums and avoid the -ambiguity altogether, but it seems a shame to give them up. +the entire literal type in a string -- e.g. ``"Literal[Color.RED]"``. Literals, enums, and Any -'''''''''''''''''''''''' +------------------------ Another ambiguity is when the user attempts to use some expression that -is meant to be an enum but is actually of type ‘Any’. For example, +is meant to be an enum but is actually of type ``Any``. For example, suppose a user attempts to import an enum from a package with no type hints:: from typing import Literal from lib_with_no_types import SomeEnum # SomeEnum has type 'Any'! - # Signature is equivalent to `func(x: Literal[Any]) -> None` - # due to the bad import - def func(x: Literal[SomeEnum.FOO]) -> None: pass + # x has type `Literal[Any]` due to the bad import + x: Literal[SomeEnum.FOO] -Normally, the type checker would be fine with ``func``: it's usually safe to -substitute ``Any`` anywhere a type is expected. +Because ``Literal`` may not be parameterized by ``Any``, this program +is *illegal*: the type checker should report an error with the last line. -However, in this case the type checker should report an error: types like -``Literal[Any]`` are currently considered illegal. Although ``Any`` can -serve as a placeholder for any arbitrary *type*, it is **not** allowed to -serve as a placeholder for any arbitrary *value*. +In short, while ``Any`` may effectively be used as a placeholder for any +arbitrary *type*, it is currently **not** allowed to serve as a placeholder +for any arbitrary *value*. -This decision is provisional and may be changed at a future date. Inferring types for literal expressions ---------------------------------------- +======================================= -This section describes how to infer the correct type for literal expressions. -E.g. under what circumstances should literal expressions like ``"foo"`` -have an inferred type of ``Literal["foo"]`` vs ``str``? +This section describes under what circumstances some expression should have +an inferred Literal type. -In general, type checkers are expected to be conservative and bias -towards inferring standard types like ``str``. Type checkers should -infer ``Literal[...]`` only in context where a Literal type is -explicitly requested. +For example, under what circumstances should literal expressions like ``"blue"`` +have an inferred type of ``Literal["blue"]`` vs ``str``? + +In short, type checkers are expected to be conservative and bias towards +inferring standard types like ``str``. Type checkers should infer ``Literal[...]`` +only in contexts where a Literal type is explicitly requested. Variable assignment -''''''''''''''''''' +------------------- When assigning a literal expression to an unannotated variable, the inferred type of the variable is the original base type, not ``Literal[...]``. For example:: - foo = "hello" - reveal_type(foo) # Revealed type is 'str' + border_color = "blue" + reveal_type(border_color) # Revealed type is 'str' This helps ensure we don't break the semantics of any existing code. If the user wants ``foo`` to have a literal type, they must explicitly add an annotation:: - foo: Literal["hello"] = "hello" - reveal_types(foo) # Revealed type is 'Literal["hello"]' + border_color: Literal["blue"] = "blue" + reveal_types(border_color) # Revealed type is 'Literal["blue"]' Or alternatively, use the ``Final`` qualifier:: - foo: Final = "hello" - reveal_types(foo) # Revealed type is 'Final[Literal["hello"]]' + border_color: Final = "blue" + reveal_types(border_color) # Revealed type is 'Final[Literal["blue"]]' The ``Final`` qualifier will automatically infer a ``Literal`` type in an assignment if the LHS is a literal expression, or an expression of -type ``Literal[…]``. +type ``Literal[...]``. **TODO:** Link to the PEP draft for the ``Final`` qualifier once it's ready. @@ -324,31 +350,32 @@ try using ``Literal`` as a qualifier:: foo: Literal = "hello" # Illegal! -Although this looks reasonable, we feel type checkers should *reject* -constructs like these: while ``Final`` and ``ClassVar`` are *qualifiers* -and so infer their parameters, ``Literal`` is a *type* and should not. +Type checkers should *reject* lines like these. Unlike ``Final`` and ``ClassVar``, +``Literal`` is a *type*, not a *qualifier*. Only qualifiers should infer their +parameters. -**Note 2:** It may sometimes be possible to use the broader context -to determine whether some variable should have a Literal type or not. -For example, in the following function, ``foo`` is only ever used as -an input to a function that expects ``Literal["blah"]`` which means -it’s theoretically possible to infer that foo has type ``Literal["blah"]``:: +**Note 2:** Type checkers are only expected to use the context available +to them within the current statement to infer the type of the variable. +They may *optionally* use additional context to infer more precise types. +For example:: - def expects_blah(x: Literal["blah"]) -> None: ... + def expects_blah(x: Literal[4]) -> None: ... def test() -> None: - foo = "blah" + foo = 4 expects_blah(foo) -This PEP proposes that type checkers are **not** expected to handle these -cases: it is ok to infer that ``foo`` has type ``str``. +In this program, it is theoretically possible for a type checker to deduce that +``foo`` is only ever used as input to a function that expects ``Literal[4]`` +and so infer that ``foo`` must have type ``Literal[4]``. -However, it's an open question whether type checkers are permitted to *try* -handling these more complex cases on a best-effort basis. That is, are -type checkers *obligated* to infer that ``foo`` has type ``str``? +While type checkers *may* perform this kind of analysis, they are not obligated +to do so. It is ok to infer that ``foo`` has type ``int``, since there is no +context present in the assignment statement itself that would suggest that ``foo`` +should be a literal type. Type inference inside calls -''''''''''''''''''''''''''' +--------------------------- When a literal is used inside of a function call, it will be inferred as either the original type or the Literal type based on context. For @@ -376,34 +403,19 @@ to be literals. For example:: *and* the original type -- perhaps for legacy purposes -- they should implement a fallback overload. See `Interactions with overloads`_. -Miscellaneous interactions --------------------------- +Interactions with other types and features +========================================== -This section discusses how literal types interact with other existing types. +This section discusses how Literal types interact with other existing types. -Intelligent indexing of structured data: Interactions with TypedDict, Tuple, NamedTuples, and getattr -''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' +Intelligent indexing of structured data +--------------------------------------- -Literals can be used to "intelligently index" into structured types -TypedDicts, NamedTuple, and classes. (This is not an exhaustive list). +Literals can be used to "intelligently index" into structured types like +NamedTuple, classes, and TypedDict. (Note: this is not an exhaustive list). For example, type checkers should infer the correct value type when -indexing into a TypedDict using a string literal that corresponds to -one of the available keys:: - - Foo = TypedDict('Foo', { - 'key1': int, - 'key2': str, - }) - - a: Final = "key1" - b: Final = "some other string" - - f: Foo - reveal_type(f[a]) # Revealed type is 'int' - f[b] # Error: 'Foo' does not contain a key named 'some other string' - -We require similar behavior when indexing into a tuple or NamedTuple:: +indexing into a tuple using an int key that corresponds a valid index:: a: Final = 0 b: Final = 5 @@ -412,7 +424,7 @@ We require similar behavior when indexing into a tuple or NamedTuple:: reveal_type(some_tuple[a]) # Revealed type is 'int' some_tuple[b] # Error: 5 is not a valid index into the tuple -...and when using functions like getattr:: +We expect similar behavior when using functions like getattr:: class Test: def __init__(self, param: int) -> None: @@ -430,21 +442,12 @@ We require similar behavior when indexing into a tuple or NamedTuple:: getattr(t, c) # Error: 'Test' does not have attribute named 'blah' These interactions will most likely need to be added to type checkers on -an ad-hoc basis. This is a little unfortunate: it would have been nice to -unify these interactions by adding something like TypeScript’s -`index types `_ and ``keyof`` operator, which lets -you encode the idea that some key (e.g. a literal string) is a member of -some object. - -We currently do not plan on adding a similar concept to Python. Python -has many different kinds of structured data beyond just objects -(classes, objects, TypedDict, tuples, NamedTuples…) and it’s unclear -what the ramifications of attempting to unify all these different -concepts using this idea might be. It may be worth attempting to -unify this behavior in the future, but probably not as a part of this PEP. +an ad-hoc basis. + +**TODO:** Link to the PEP for TypedDict once it's ready. Interactions with overloads -''''''''''''''''''''''''''' +--------------------------- Literal types and overloads do not need to interact in a special way: the existing rules work fine. @@ -481,16 +484,14 @@ mandates that whenever we add literal types to some existing API, we also always include a fallback overload to maintain backwards-compatibility. Interactions with generics -'''''''''''''''''''''''''' +-------------------------- Types like ``Literal[3]`` are meant to be just plain old subclasses of ``int``. This means you can use types like ``Literal[3]`` anywhere you could use normal types, such as with generics. -For example, suppose we want to construct a type representing a -2-dimensional Matrix which can be parameterized by two literal ints -representing the number of rows and columns respectively. Such a type -could be built using the existing generics system like so:: +This means that it is legal to parameterize generic functions or +classes using Literal types:: A = TypeVar('A', bound=int) B = TypeVar('B', bound=int) @@ -498,27 +499,36 @@ could be built using the existing generics system like so:: # A simplified definition for Matrix[row, column] class Matrix(Generic[A, B]): - def __init__(self, elements: List[List[int]]) -> None: ... def __add__(self, other: Matrix[A, B]) -> Matrix[A, B]: ... def __matmul__(self, other: Matrix[B, C]) -> Matrix[A, C]: ... def transpose(self) -> Matrix[B, A]: ... - Foo: Matrix[Literal[2], Literal[3]] = Matrix(...) - Bar: Matrix[Literal[3], Literal[7]] = Matrix(...) + foo: Matrix[Literal[2], Literal[3]] = Matrix(...) + bar: Matrix[Literal[3], Literal[7]] = Matrix(...) - reveal_type(Foo @ Bar) # Revealed type is Matrix[Literal[2], Literal[7]] - Bar @ Foo # Error, Foo doesn't match expected type Matrix[Literal[7], Literal[int]] + baz = foo @ bar + reveal_type(baz) # Revealed type is 'Matrix[Literal[2], Literal[7]]' -This class definition is not perfect: it would not prohibit users from -constructing less precise types like ``Matrix[int, int]`` due to the -typevar bound, for example. +Similarly, it is legal to construct TypeVars with value restrictions +or bounds involving Literal types:: -We considered several different proposals for addressing this gap -but ultimately rejected all of them and decided to defer the problem -of integer generics to a later date. See `Rejected or out-of-scope ideas`_ + T = TypeVar('T', Literal["a"], Literal["b"], Literal["c"]) + S = TypeVar('S', bound=Literal["foo"]) + +...although it is unclear when it would ever be useful to do so. + +**Note:** Literal types and generics deliberately interact in only very +basic and limited ways. In particular, libraries that want to typecheck +code containing an heavy amount of numeric or numpy-style manipulation will +almost certainly likely find Literal types as proposed in this PEP to be +insufficient for their needs. + +We considered several different proposals for fixing this, but ultimately +decided to defer the problem of integer generics to a later date. See +`Rejected or out-of-scope ideas`_ for more details. Interactions with asserts and other checks -'''''''''''''''''''''''''''''''''''''''''' +------------------------------------------ Type checkers should narrow the type of variables when they are compared directly against other literal types. For example:: @@ -532,12 +542,43 @@ directly against other literal types. For example:: assert x == "bar" expects_bar(x) -Type checkers may optionally perform additional analysis and narrowing. - -**Note:** The exact details of this section may be subject to change. +This includes with enums. For example, the type checker should be capable +of inferring that the final ``else`` statement in the following function +is unreachable:: + + class Status(Enum): + SUCCESS = 0 + PARSE_ERROR = 1 + INVALID_DATA = 2 + FATAL_ERROR = 3 + + def parse_status(status: Status) -> None: + if status is Status.SUCCESS: + print("Success!") + elif status is Status.PARSE_ERROR: + print("Unable to deserialize data") + elif status is Status.INVALID_DATA: + print("The given data is invalid because...") + elif status is Status.FATAL_ERROR: + print("Unexpected fatal error...") + else: + # Error should not be reported by type checkers that + # ignore errors in unreachable blocks + print("Nonsense" + 100) + +This behavior is technically not new: this behavior is +`already codified within PEP 484 `_. However, many type +checkers (such as mypy) do not yet implement this behavior. Once literal +types are introduced, it will become easier to do so: we can model +enums as being approximately equal to the union of their values. So, +``Status`` would be treated as being approximately equal to +``Literal[Status.SUCCESS, Status.PARSE_ERROR, Status.INVALID_DATA, Status.FATAL_ERROR]``. + +Type checkers may optionally perform additional analysis and narrowing +beyond what is described above. Interactions with Final types -''''''''''''''''''''''''''''' +----------------------------- The interactions between final and literal types were previously mentioned above, but to reiterate: if a variable is annotated as @@ -616,7 +657,7 @@ following snippet crashes when run using Python 3.7:: Running this yields the following exception:: - TypeError: Tuple[t0, t1, …]: each t must be a type. Got 1. + TypeError: Tuple[t0, t1, ...]: each t must be a type. Got 1. We don’t want users to have to memorize exactly when it’s ok to elide ``Literal``, so we require ``Literal`` to always be present. @@ -657,6 +698,10 @@ something similar to how .. _typescript-index-types: https://www.typescriptlang.org/docs/handbook/advanced-types.html#index-types +.. _newtypes: https://www.python.org/dev/peps/pep-0484/#newtype-helper-function + +.. _pep-484-enums: https://www.python.org/dev/peps/pep-0484/#support-for-singleton-types-in-unions + Copyright =========