Welcome to the Comprehensive Guide to typing-extensions
typing-extensions
is a package designed to provide forward-compatible typing features that are not yet available in the standard Python typing
module. It is essential for developers who want to use new type hinting features in older versions of Python or stay ahead with enhanced flexibility while dealing with type annotations.
Key APIs and How to Use Them
Below are some of the popular features and APIs provided by typing-extensions
, including usage examples:
1. Literal
: Restricting Values
Literal
allows defining a variable that can only hold specific predefined values:
from typing_extensions import Literal def get_status(status: Literal["active", "inactive", "pending"]) -> str: return f"Status is {status}" print(get_status("active")) # Valid # print(get_status("error")) # Invalid: Raises a type checker error
2. TypedDict
: Dictionary with Typed Keys and Values
Create dictionaries where keys and values have predefined types:
from typing_extensions import TypedDict class User(TypedDict): id: int username: str is_active: bool user: User = {"id": 1, "username": "john_doe", "is_active": True}
3. Protocol
: Define Structural Subtyping
Protocols enable structural subtyping (duck typing) to ensure that a type implements specific methods:
from typing_extensions import Protocol class Greeter(Protocol): def greet(self) -> str: pass class EnglishGreeter: def greet(self) -> str: return "Hello!" def greet_all(greeter: Greeter): print(greeter.greet()) english = EnglishGreeter() greet_all(english)
4. Final
: Prevent Overriding Variables or Methods
Make certain fields or methods immutable:
from typing_extensions import Final MAX_CONNECTIONS: Final = 10 # MAX_CONNECTIONS = 15 # This will cause a MyPy error
5. Annotated
: Adding Extra Metadata to Types
Annotated
allows attaching additional metadata to a type:
from typing_extensions import Annotated def process_data(data: Annotated[int, "Data must be positive"]) -> int: if data < 0: raise ValueError("Invalid data") return data
Building a Simple Application Using typing-extensions
Let’s create a simple user management system with the help of typing-extensions
:
from typing_extensions import Literal, TypedDict, Protocol # Define user schema using TypedDict class User(TypedDict): id: int name: str status: Literal["active", "inactive", "pending"] # Define output protocol class UserManagerProtocol(Protocol): def list_users(self) -> list[User]: pass # Implement the UserManagerProtocol class UserManager: def __init__(self): self.users = [ {"id": 1, "name": "Alice", "status": "active"}, {"id": 2, "name": "Bob", "status": "inactive"}, ] def list_users(self) -> list[User]: return self.users # Application Logic def main(): user_manager: UserManagerProtocol = UserManager() users = user_manager.list_users() for user in users: print(f"User {user['name']} is {user['status']}") if __name__ == "__main__": main()
Why typing-extensions
is a Must-Have
If you’re working on robust Python code with strong typing, typing-extensions
becomes indispensable. It bridges the gap between different Python versions and provides cutting-edge features to make your code more intelligible and less error-prone.