8  Type hints cheat sheet

See also: https://mypy.readthedocs.io/en/latest/cheat_sheet_py3.html

8.1 Summary Comparison Table

Feature Python 3.5-3.8 Python 3.9+ Python 3.10+ Python 3.11+
Basic types List[int] list[int] list[int] list[int]
Union types Union[int, str] Union[int, str] int \| str int \| str
Optional Optional[str] Optional[str] str \| None str \| None
Type alias Vector = List[float] Vector = list[float] Vector: TypeAlias = list[float] Same as 3.10
Self type Use TypeVar Use TypeVar Use TypeVar Self
NotRequired Not available Not available Not available NotRequired[T]
TypeGuard Not available Not available TypeGuard TypeGuard

8.2 Basic Syntax

# Variable annotations
name: str = "Alice"
age: int = 30
height: float = 5.9
is_student: bool = False

# Function annotations
def greet(name: str) -> str:
    """Say hello to someone."""
    return f"Hello, {name}!"

# Function with multiple parameters
def add(x: int, y: int) -> int:
    """Add two numbers."""
    return x + y

8.3 Built-in types

# Primitive types
x: int = 42
y: float = 3.14
z: str = "hello"
flag: bool = True
data: bytes = b"binary data"

# None type
result: None = None
def print_message(msg: str) -> None:
    """Print a message."""
    print(msg)

8.4 Collection Types

from typing import List, Dict, Tuple, Set, FrozenSet

# Lists
num: list[int] = [1, 2, 3]  # Python 3.9+
num: List[int] = [1, 2, 3]  # Python 3.5+

# Dictionaries
scores: dict[str, int] = {"Alice": 95, "Bob": 87}  # Python 3.9+
scores: Dict[str, int] = {"Alice": 95, "Bob": 87}  # Python 3.5+

# Tuples (fixed size)
point: tuple[float, float] = (3.0, 4.0)  # Python 3.9+
point: Tuple[float, float] = (3.0, 4.0)  # Python 3.5+

# Tuples (variable size)
numbers: tuple[int, ...] = (1, 2, 3, 4, 5)  # Python 3.9+
numbers: Tuple[int, ...] = (1, 2, 3, 4, 5)  # Python 3.5+

# Sets
unique_ids: set[int] = {1, 2, 3}  # Python 3.9+
unique_ids: Set[int] = {1, 2, 3}  # Python 3.5+

# Frozen sets
frozen: frozenset[str] = frozenset(["a", "b", "c"])  # Python 3.9+
frozen: FrozenSet[str] = frozenset(["a", "b", "c"])  # Python 3.5+

8.5 Union Types

from typing import Union
from typing import Optional

# Python 3.10+ pipe syntax (Preferred)
id_number: int | str = 42
id_number: int | str = "ABC123"

# Union (either type)
id_number: Union[int, str] = 42
id_number: Union[int, str] = "ABC123"

# All of these are the same:
name: str | None = "Alice" # Python 3.10+
name: Union[str, None] = "Alice"
name: Optional[str] = "Alice"  

8.6 Callable types

from typing import Callable

# Callable types
def apply_func(f: Callable[[int], str], x: int) -> str:
    """Apply function f to x."""
    return f(x)

# More specific callable
operation: Callable[[int, int], int] = lambda x, y: x + y
def g(x: int) -> str:
    return str(x)
apply_func(g, 5)
'5'

8.7 Type variables

from typing import TypeVar

T = TypeVar('T')
def identity(x: T) -> T:
    """Return the same value."""
    return x
print(type(identity(9)))
identity(9)
<class 'int'>
9

8.8 Generic Classes

# Generic classes
from typing import Generic
class Box(Generic[T]):
    def __init__(self, value: T) -> None:
        """Initialize box with value."""
        self._value = value
    
    @property
    def value(self) -> T:
        """Get the value."""
        return self._value
box_int: Box[int] = Box(42)
box_int.value
42
box_str: Box[str] = Box("hello")
box_str.value
'hello'

8.9 Protocol (similar to interface)

from typing import Protocol

class Drawable(Protocol):
    def draw(self) -> None:
        """Draw the object."""
        ...

class Circle:
    def draw(self) -> None:
        """Draw a circle."""
        print("Drawing circle")

def render(obj: Drawable) -> None:
    """Render any drawable object."""
    obj.draw()

# Circle implements Drawable protocol
render(Circle())  # Works!
Drawing circle

8.10 Literal Types

from typing import Literal

def open_file(filename: str, mode: Literal["r", "w", "a"]) -> None:
                                          # ^^^^^^^^^^^^ IDE autocheck args
    """Open a file in specified mode."""
    pass
open_file(filename="x", mode="b")
                        # ^^^^^^ Squzzy line will appear

8.11 Type Aliases

from typing import TypeAlias  # Python 3.10+

# Simple alias
Vector = list[float]
Matrix = list[list[float]]

# Python 3.10+ syntax
UserId: TypeAlias = int
UserDict: TypeAlias = dict[UserId, str]

# Complex alias
JSON = dict[str, Union[str, int, float, bool, None, list['JSON'], dict[str, 'JSON']]]

# Using aliases
def process_vector(v: Vector) -> float:
    """Process a vector."""
    return sum(v)

8.12 TypedDict

from typing import TypedDict
from typing import NotRequired  # Python 3.11+

# Define structured dictionary
class Person(TypedDict):
    name: str
    age: int
    email: str

# Optional fields
class Employee(TypedDict):
    name: str
    age: int
    email: NotRequired[str]  # Optional field

# Using TypedDict
person: Person = {
    "name": "Alice",
    "age": 30,
    "email": "alice@example.com"
}

# Python 3.8+ total=False for all optional
class Config(TypedDict, total=False):
    debug: bool
    timeout: int
    host: str

8.13 Overloading

from typing import overload

@overload
def process(x: int) -> str:
    ...

@overload
def process(x: str) -> int:
    ...

def process(x: Union[int, str]) -> Union[str, int]:
    """Process input based on type."""
    if isinstance(x, int):
        return str(x)
    else:
        return len(x)

8.14 Final and Constants

from typing import Final
from typing import final # Final methods and classes

# Constants
MAX_SIZE: Final[int] = 100
API_URL: Final = "https://api.example.com"  # Type inferred

@final
class Constants:
    """Cannot be subclassed."""
    pass

class Base:
    @final
    def method(self) -> None:
        """Cannot be overridden."""
        pass