Introduction to Typing-Extensions
The typing-extensions
package is a must-have library for Python developers seeking to leverage type annotations and maintain compatibility across different Python versions. It provides backports of new type-hinting features introduced in newer versions of the typing
module, ensuring that your code remains forward-compatible. This blog post will explore various APIs provided by typing-extensions
with practical code examples and conclude with a simple app that uses multiple introduced APIs.
Why Typing-Extensions?
The Python typing
module evolves rapidly, and the typing-extensions
package serves as a bridge for developers working with older Python versions. It allows access to cutting-edge type-checking features such as TypedDict
, Protocol
, Literal
, and more, regardless of your Python runtime.
Useful APIs in Typing-Extensions
1. Literal
The Literal
type annotation allows you to specify a restricted set of constant values that a variable or function argument can accept.
from typing_extensions import Literal def order_status(status: Literal["pending", "shipped", "delivered"]) -> str: return f"Your order is {status}." print(order_status("pending")) # Valid # print(order_status("cancelled")) # Type checker will flag this as invalid
2. TypedDict
The TypedDict
class allows you to define dictionaries with a specific key-value structure, making them great for specifying JSON-like objects.
from typing_extensions import TypedDict class Product(TypedDict): id: int name: str price: float product: Product = {"id": 1, "name": "Laptop", "price": 999.99} print(product)
3. Protocol
The Protocol
class provides structural subtyping (also known as “duck typing”), enabling you to specify that a class must implement certain methods or attributes.
from typing_extensions import Protocol class Greeter(Protocol): def greet(self) -> str: ... class FriendlyGreeter: def greet(self) -> str: return "Hello!" def introduce(greeter: Greeter): print(greeter.greet()) friendly = FriendlyGreeter() introduce(friendly)
4. Final
Use Final
to declare a variable or method that cannot be overridden or reassigned.
from typing_extensions import Final MAX_USERS: Final = 100 # MAX_USERS = 200 # This will raise an error in type-checking
5. Concatenate
and ParamSpec
These are advanced tools for defining flexible higher-order functions with accurate type hints.
from typing_extensions import Concatenate, ParamSpec from typing import Callable P = ParamSpec("P") def add_logging(f: Callable[Concatenate[str, P], str]) -> Callable[P, str]: def wrapper(*args: P.args, **kwargs: P.kwargs) -> str: print("Logging: Called with", args, kwargs) return f("INFO", *args, **kwargs) return wrapper def process_data(level: str, value: int) -> str: return f"{level}: Processed {value}" logged_process = add_logging(process_data) print(logged_process(42))
6. Self
The Self
annotation improves type annotations for methods that return an instance of the same class.
from typing_extensions import Self class Builder: def set_value(self, value: str) -> Self: self.value = value return self builder = Builder().set_value("Demo")
7. Annotated
Use Annotated
to attach metadata to type hints, often used in frameworks for validation or documentation.
from typing_extensions import Annotated from typing import Any def process(value: Annotated[str, "Must be a non-empty string"]) -> None: if not value: raise ValueError("Invalid input") print(value) process("Valid Input")
Example Application
Using multiple APIs introduced above, let’s create a type-safe Order Management System.
from typing_extensions import Literal, TypedDict, Protocol class Product(TypedDict): id: int name: str price: float def display_status(status: Literal["pending", "shipped", "delivered"]) -> str: return f"Order Status: {status}" class Greeter(Protocol): def greet(self) -> str: ... class FriendlyGreeter: def greet(self) -> str: return "Welcome to the Order Management System!" # Sample Usage product = {"id": 1, "name": "Laptop", "price": 999.99} greeter = FriendlyGreeter() print(greeter.greet()) print(display_status("pending")) print(f"Product Details: {product}")
Conclusion
The typing-extensions
package is an invaluable tool for Python developers, making it easier to write robust and maintainable code with advanced type hinting capabilities. Whether you’re developing libraries, APIs, or applications, leveraging typing-extensions
ensures forward-compatibility and improved code quality.