Introduction to Typing Extensions: Boosting Python’s Type Hints
The typing-extensions library is a vital tool for Python developers who want to leverage advanced typing features. It provides backports of type hints introduced in newer Python versions, enabling compatibility and early adoption of modern typing features. This library is particularly useful for projects that need to run on multiple Python versions.
Key APIs Offered by Typing-Extensions
Below is a detailed overview of the most important APIs provided by typing-extensions
, along with practical examples:
1. Literal
The Literal
type lets you specify that a variable can only take on a defined set of values.
from typing_extensions import Literal def greet(lang: Literal["EN", "ES", "FR"]) -> str: if lang == "EN": return "Hello" elif lang == "ES": return "Hola" elif lang == "FR": return "Bonjour" print(greet("EN")) # -> Hello
2. TypedDict
TypedDict
allows you to define dictionaries with specific key-value types.
from typing_extensions import TypedDict class User(TypedDict): name: str age: int user: User = {"name": "Alice", "age": 25}
3. Protocol
Protocol
allows you to define structural subtyping (duck typing).
from typing_extensions import Protocol class Flyer(Protocol): def fly(self) -> None: ... class Bird: def fly(self) -> None: print("Flying!") def make_it_fly(flyer: Flyer) -> None: flyer.fly() bird = Bird() make_it_fly(bird)
4. Annotated
The Annotated
type lets you attach metadata to types.
from typing_extensions import Annotated def process_data(data: Annotated[int, "Must be a positive integer"]) -> None: print(data) process_data(10) # -> 10
5. Concatenate
Concatenate
enables more precise types in callable signatures.
from typing_extensions import Concatenate, ParamSpec P = ParamSpec("P") def log_args(callback: Callable[Concatenate[str, P], None], message: str, *args: P.args, **kwargs: P.kwargs) -> None: callback(message, *args, **kwargs)
6. Final
Final
prevents reassignment of variables
from typing_extensions import Final PI: Final = 3.14159
An Example Application Using Typing-Extensions
Here’s a small application that combines several typing-extensions
features:
from typing_extensions import TypedDict, Literal, Protocol class Config(TypedDict): log_level: Literal["DEBUG", "INFO", "WARNING", "ERROR"] output_dir: str class Runner(Protocol): def run(self) -> None: ... class ScriptRunner: def run(self) -> None: print("Script is running!") def execute_task(config: Config, runner: Runner) -> None: print(f"Running with config: {config}") runner.run() my_config: Config = {"log_level": "DEBUG", "output_dir": "/tmp/logs"} my_runner = ScriptRunner() execute_task(my_config, my_runner)
This example showcases the combination of TypedDict
, Literal
, and Protocol
for robust code with type safety.
Why You Should Use Typing Extensions
By making use of typing-extensions
, Python developers can ensure their code is future-proof, maintainable, and compatible across multiple Python versions. This library allows you to write more expressive type annotations, improving code clarity and reducing runtime errors.