Enhance Python Type Hinting with Typing Extensions Explained

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.

Leave a Reply

Your email address will not be published. Required fields are marked *