Python typing.Literal: Restrict to Specific Values

Python's typing.Literal type hint enforces compile-time constraints by restricting a variable, parameter, or return value to an explicit set of distinct, predefined values. While traditional type annotations like str or int permit any valid value of those types, Literal narrows the allowed inputs to exact values such as specific strings, integers, booleans, or None. This article explains the syntax of typing.Literal, how static type checkers interpret it, and how it compares to alternative patterns like enumerations.

Understanding typing.Literal

Introduced in Python 3.8 via PEP 586, Literal is part of the standard typing module. It tells static analysis tools—such as Mypy, Pyright, and IDE linters—that an expression must equal one of the explicitly listed literal values.

from typing import Literal

# Restricting a variable to three specific string values
Mode = Literal["read", "write", "append"]


def open_file(path: str, mode: Mode) -> None:
    pass


open_file("data.txt", "read")  # Valid
open_file("data.txt", "execute")  # Type checker error: Argument 2 has incompatible type "Literal['execute']"

Supported Literal Types

Literal only accepts specific primitive values:

You can also combine different data types within a single Literal definition:

from typing import Literal

Status = Literal["pending", 0, False]

Passing multiple arguments into Literal[...] is equivalent to combining them with typing.Union. Therefore, Literal["read", "write"] is treated by type checkers identically to Union[Literal["read"], Literal["write"]].

Runtime vs. Static Behavior

Python's standard type hints, including Literal, do not automatically enforce constraints at runtime. If an invalid value is passed to a function at runtime, Python will execute the code unless explicit validation logic or third-party validation libraries (like Pydantic) are used.

# This executes without a Python runtime exception unless checked:
open_file("data.txt", "invalid_mode")

The restriction is enforced during development by static analysis engines. When using an IDE or running mypy script.py, the type checker cross-references the assigned argument against the allowed literals and reports an error before the code is deployed.

Common Use Cases

  1. Replacing "Magic Strings": Many APIs rely on specific string parameters (e.g., HTTP methods "GET", "POST", "PUT"). Using Literal documents the valid choices directly in the signature and provides autocompletion in code editors.
  2. Function Overloading with Discriminated Unions: Literal can determine return types dynamically using @typing.overload. For example, a function can return bytes when passed Literal["bytes"] and str when passed Literal["text"].

typing.Literal vs. enum.Enum

While enum.Enum also restricts inputs to specific members, Literal is preferred when: