3 min read

Python Typing Module

The typing module provides runtime support for type hints. The most fundamental support consists of the types Any, Union, Callable, TypeVar, and Generic. For full specification, please see PEP 484.

Importing the Module

import typing

Common Types

Since Python 3.9, many built-in collection types (like list, dict, tuple) support subscription (e.g., list[int]). However, the typing module provides aliases for these and other types, which are necessary for older Python versions or for more complex typing scenarios.

List, Dict, Tuple, Set

from typing import List, Dict, Tuple, Set

# List of integers
numbers: List[int] = [1, 2, 3]

# Dictionary with string keys and float values
prices: Dict[str, float] = {'apple': 1.5, 'banana': 2.0}

# Tuple with specific types
person: Tuple[str, int] = ('Alice', 30)

# Set of strings
tags: Set[str] = {'red', 'green', 'blue'}

Optional and Union

  • Optional[T]: Equivalent to Union[T, None].
  • Union[T1, T2]: Indicates that a value can be either type T1 or T2.
from typing import Optional, Union

def greet(name: Optional[str] = None) -> str:
    if name is None:
        return "Hello, stranger"
    return f"Hello, {name}"

def process(value: Union[int, str]) -> None:
    if isinstance(value, int):
        print(f"Processing integer: {value}")
    else:
        print(f"Processing string: {value}")

Any

Any is a special type indicating that a value can be of any type. Static type checkers will treat every type as being compatible with Any and Any as being compatible with every type.

from typing import Any

def do_something(x: Any) -> Any:
    return x

Type Aliases

You can define aliases for complex types to make code more readable.

from typing import List, Tuple, Dict

Vector = List[float]
ConnectionOptions = Dict[str, str]
Address = Tuple[str, int]
Server = Tuple[Address, ConnectionOptions]

def connect(server: Server) -> None:
    pass

NewType

NewType creates distinct types that are treated differently by the type checker but are identical at runtime.

from typing import NewType

UserId = NewType('UserId', int)

def get_user_name(user_id: UserId) -> str:
    return "User"

# Type checker ensures we pass a UserId, not just any int
user_a = UserId(12345)
get_user_name(user_a) 

# get_user_name(12345) # This would be a type error

Callable

Callable is used to type hints for functions or other callable objects. The syntax is Callable[[Arg1Type, Arg2Type], ReturnType].

from typing import Callable

def apply_func(x: int, func: Callable[[int], int]) -> int:
    return func(x)

def square(x: int) -> int:
    return x * x

print(apply_func(5, square)) # Output: 25

Generics and TypeVar

TypeVar is used to define generic types.

from typing import TypeVar, Sequence

T = TypeVar('T')      # Declare type variable

def first(seq: Sequence[T]) -> T:
    return seq[0]

print(first([1, 2, 3])) # Output: 1 (inferred as int)
print(first('abc'))     # Output: 'a' (inferred as str)

programming/python/python