An Introduction to Typing Extensions in Python
typing-extensions
is a Python module that allows developers to experiment with features that might one day belong in the built-in typing
module. It acts as a bridge for enabling forward compatibility and extending the functionality of type hints. If you want to write better, more maintainable Python codes, it’s a must-have in your toolbox, especially when working with older Python versions.
Top Features and APIs of Typing Extensions
Let’s explore the treasure trove of APIs provided by typing-extensions
, along with code examples for each.
1. Annotated
for Metadata
This API allows you to attach additional metadata to type hints.
from typing_extensions import Annotated def process_data(x: Annotated[int, "This is an integer"]): print(f"The value is {x}.") process_data(42)
2. Literal
for Fixed Values
Literal
restricts input to a predefined set of values:
from typing_extensions import Literal def get_status(status: Literal["success", "error"]): print(f"Status: {status}") get_status("success")
3. TypedDict
for Typed Dictionaries
Define dictionaries with key-specific types:
from typing_extensions import TypedDict class UserInfo(TypedDict): name: str age: int user: UserInfo = {"name": "Alice", "age": 30} print(user)
4. Protocol
for Structural Subtyping
Protocol
provides a way to define interfaces:
from typing_extensions import Protocol class Greetable(Protocol): def greet(self) -> str: ... class Person: def greet(self) -> str: return "Hello!" def welcome(greetable: Greetable): print(greetable.greet()) p = Person() welcome(p)
5. Final
for Constants
Use Final
to declare variables that should not be reassigned:
from typing_extensions import Final PI: Final = 3.14159
6. Self
in Method Annotations
Use Self
to enhance method definitions:
from typing_extensions import Self class MyClass: def reset(self) -> Self: return self
7. Never
for Functions That Don’t Return
Use Never
for functions guaranteed to never return:
from typing_extensions import Never def error() -> Never: raise RuntimeError("This function always raises an error.")
8. Required
and NotRequired
in TypedDict
Used to mark dictionary keys as required or optional:
from typing_extensions import TypedDict, Required, NotRequired class Movie(TypedDict): title: str release_year: Required[int] rating: NotRequired[float] movie: Movie = {"title": "Inception", "release_year": 2010}
A Comprehensive Application Example
Let’s combine multiple typing-extensions
APIs to build a simple application that processes user profiles:
from typing_extensions import Literal, Annotated, TypedDict, Final, Protocol # A constant API_VERSION: Final = "1.0" # Define user profile class UserProfile(TypedDict): username: str access_level: Literal["admin", "user"] age: Annotated[int, "User's age"] # Define a Protocol for a user service class UserService(Protocol): def get_profile(self, username: str) -> UserProfile: ... # Example implementation class MockUserService: def get_profile(self, username: str) -> UserProfile: return {"username": username, "access_level": "admin", "age": 30} def print_user_info(user_svc: UserService, username: str): profile = user_svc.get_profile(username) print(f"User: {profile['username']}, Access Level: {profile['access_level']}") # Run app service = MockUserService() print_user_info(service, "Alice") print(f"API Version: {API_VERSION}")
Final Thoughts
typing-extensions
empowers Python developers with type hinting features that are either experimental or not yet available in standard typing
. Whether you’re developing libraries, APIs, or robust Python applications, it opens up new possibilities for clean and maintainable code.