Introduction to Typing Extensions
The typing-extensions
library is an invaluable tool for Python developers who want to stay ahead of the curve in type annotations. Originally, type hinting support was introduced in Python 3.5 via the typing
module. However, maintaining backward compatibility with older Python versions while adopting newer type-hinting features introduced in recent Python releases can be challenging. This is where typing-extensions
shines—it provides a backport of fancy and upcoming type-hinting features without requiring the latest Python version.
Why typing-extensions
Matters
With typing-extensions
, you get access to advanced capabilities such as flexible typing structures, experimental type hints, and forward-looking features. Let’s dive into some of the most useful APIs provided by typing-extensions
, complete with examples to help you get started.
Commonly Used APIs in Typing Extensions
1. Literal
Literal
allows you to specify that a variable must take on a specific value from a predefined set.
from typing_extensions import Literal def process_status(status: Literal["success", "error", "pending"]) -> str: if status == "success": return "Operation completed!" elif status == "error": return "An error occurred!" elif status == "pending": return "Operation is in progress." print(process_status("success"))
2. TypedDict
TypedDict
helps you define dictionary structures with fixed key-value pairs.
from typing_extensions import TypedDict class User(TypedDict): id: int username: str is_active: bool user: User = {"id": 1, "username": "johndoe", "is_active": True} print(user["username"]) # Output: johndoe
3. Protocol
Using Protocol
, you can define structural subtyping (duck typing) for classes. It focuses more on method signatures rather than class inheritance.
from typing_extensions import Protocol class Greeter(Protocol): def greet(self) -> str: ... class Person: def greet(self) -> str: return "Hello!" def greet_person(person: Greeter) -> None: print(person.greet()) p = Person() greet_person(p) # Output: Hello!
4. Final
Final
ensures that a variable or method cannot be overridden or reassigned.
from typing_extensions import Final API_URL: Final = "https://api.example.com" class Base: def show_info(self) -> str: return "Base info" class Derived(Base): def show_info(self): # This will raise a type checker error if Base's show_info is annotated with Final. return "Derived info"
5. @runtime_checkable
This decorator ensures that a Protocol
can also be checked at runtime using the isinstance()
function.
from typing_extensions import Protocol, runtime_checkable @runtime_checkable class Movable(Protocol): def move(self, x: int, y: int) -> None: ... class Vehicle: def move(self, x: int, y: int) -> None: print(f"Moving to {x}, {y}") car = Vehicle() print(isinstance(car, Movable)) # Output: True
Real-World Example: A Simple Web Service
Let’s create a simple app to demonstrate typing-extensions
in action. We will use TypedDict
and Literal
for type-safe operations.
from typing_extensions import TypedDict, Literal class Item(TypedDict): id: int name: str status: Literal["available", "sold-out"] inventory = [ {"id": 1, "name": "Laptop", "status": "available"}, {"id": 2, "name": "Phone", "status": "sold-out"}, ] def get_item_status(item_id: int, inventory: list[Item]) -> str: for item in inventory: if item["id"] == item_id: return f'Item "{item["name"]}" is {item["status"]}.' return "Item not found." print(get_item_status(1, inventory))
Conclusion
The typing-extensions
library is a must-have for Python developers who wish to take full advantage of type hinting while maintaining compatibility with older Python versions. From Literal
and TypedDict
to advanced features like Protocol
and Final
, the library paves the way for writing more robust and maintainable code.
Start incorporating typing-extensions
into your projects today and level up your Python typing expertise!