Type hints make Python code self-documenting, catch bugs before runtime, and supercharge your IDE. Here's a practical guide to adopting them incrementally.
The Basics
def greet(name: str) -> str:
return f"Hello, {name}"
def add(a: int, b: int) -> int:
return a + b
# Variables
age: int = 25
names: list[str] = ["Alice", "Bob"]
config: dict[str, int] = {"timeout": 30, "retries": 3}
Optional and Union Types
from typing import Optional
# Python 3.10+
def find_user(user_id: int) -> dict | None:
if user_id in db:
return db[user_id]
return None
# Pre-3.10
def find_user(user_id: int) -> Optional[dict]:
...
Collections
# Python 3.9+ — use built-in types
def process(items: list[str]) -> dict[str, int]:
return {item: len(item) for item in items}
# Tuples with specific types
def get_coords() -> tuple[float, float]:
return (40.7128, -74.0060)
# Sets
def unique_words(text: str) -> set[str]:
return set(text.split())
TypedDict for Structured Dicts
from typing import TypedDict
class UserProfile(TypedDict):
name: str
email: str
age: int
is_active: bool
def create_user(data: UserProfile) -> None:
# IDE knows data["name"] is str, data["age"] is int
print(f"Creating user {data['name']}")
create_user({"name": "Alice", "email": "alice@example.com", "age": 30, "is_active": True})
Callable Types
from typing import Callable
def apply_operation(
values: list[int],
operation: Callable[[int], int]
) -> list[int]:
return [operation(v) for v in values]
result = apply_operation([1, 2, 3], lambda x: x * 2)
Generics
from typing import TypeVar
T = TypeVar("T")
def first(items: list[T]) -> T | None:
return items[0] if items else None
# Python 3.12+ syntax
def first[T](items: list[T]) -> T | None:
return items[0] if items else None
Dataclasses + Type Hints
from dataclasses import dataclass
@dataclass
class Order:
id: int
product: str
quantity: int
price: float
shipped: bool = False
@property
def total(self) -> float:
return self.quantity * self.price
order = Order(id=1, product="Widget", quantity=5, price=9.99)
print(order.total) # 49.95
Running mypy
pip install mypy
mypy your_script.py
# mypy catches this:
def double(x: int) -> int:
return x * 2
double("hello") # error: Argument 1 has incompatible type "str"; expected "int"
Gradual Adoption Strategy
- Start with function signatures (parameters + return types)
- Add types to dataclasses and TypedDicts
- Use
mypy --stricton new modules - Add
# type: ignoresparingly for tricky third-party code
Key Takeaways
- Type hints are optional but incredibly valuable
- Start with function signatures — biggest bang for buck
- Use
TypedDictfor dictionary structures - Run
mypyin CI to catch type errors early - Python 3.10+ has the cleanest syntax (
X | Yinstead ofUnion[X, Y])
Type hints don't slow Python down — they're stripped at runtime. But they make your code dramatically easier to read, refactor, and maintain.
🚀 Level up your AI workflow! Check out my AI Developer Mega Prompt Pack — 80 battle-tested prompts for developers. $9.99
Top comments (0)