- Published on
Python Type Hints - Write Cleaner, Safer Code
- Authors

- Name
- Sanjeev Sharma
- @webcoderspeed1
Introduction
Python type hints are no longer optional — they're a professional standard. They make your code self-documenting, catch bugs before runtime, and power popular libraries like FastAPI and Pydantic.
If you're writing Python without type hints in 2026, you're missing out on one of the language's most powerful features.
- Basic Type Hints
- Built-in Types
- Collections: List, Dict, Tuple, Set
- Optional Types
- Union Types
- TypedDict for Structured Dictionaries
- Dataclasses — Type Hints + Auto-Generated Methods
- Callable Types
- Generic Types
- Using mypy for Static Type Checking
- Real-World Example: Type-Hinted API Service
- Conclusion
Basic Type Hints
# Without type hints
def greet(name):
return "Hello, " + name
# With type hints
def greet(name: str) -> str:
return "Hello, " + name
The syntax is: variable: type for parameters and -> type after the function signature for return types.
Built-in Types
# Basic types
x: int = 10
name: str = "Alice"
price: float = 9.99
is_active: bool = True
# Function with multiple types
def add(a: int, b: int) -> int:
return a + b
def get_full_name(first: str, last: str) -> str:
return f"{first} {last}"
Collections: List, Dict, Tuple, Set
from typing import List, Dict, Tuple, Set # Python 3.8 and below
# Python 3.9+ — you can use built-in generics directly
def get_names() -> list[str]:
return ["Alice", "Bob", "Charlie"]
def get_scores() -> dict[str, int]:
return {"Alice": 95, "Bob": 88}
def get_coordinates() -> tuple[float, float]:
return (40.7128, -74.0060)
def get_unique_tags() -> set[str]:
return {"python", "coding", "tech"}
Optional Types
Use Optional when a value can be None:
from typing import Optional
def find_user(user_id: int) -> Optional[str]:
users = {1: "Alice", 2: "Bob"}
return users.get(user_id) # Returns None if not found
# Python 3.10+ shorthand using Union with None
def find_user_v2(user_id: int) -> str | None:
users = {1: "Alice", 2: "Bob"}
return users.get(user_id)
Union Types
from typing import Union
def process_id(id: Union[int, str]) -> str:
return str(id)
# Python 3.10+ shorthand
def process_id_v2(id: int | str) -> str:
return str(id)
TypedDict for Structured Dictionaries
from typing import TypedDict
class User(TypedDict):
name: str
age: int
email: str
def create_user(name: str, age: int, email: str) -> User:
return {"name": name, "age": age, "email": email}
user = create_user("Alice", 30, "alice@example.com")
print(user["name"]) # Type-safe access!
Dataclasses — Type Hints + Auto-Generated Methods
from dataclasses import dataclass
@dataclass
class Product:
name: str
price: float
in_stock: bool = True
def discount(self, percent: float) -> float:
return self.price * (1 - percent / 100)
p = Product(name="Laptop", price=999.99)
print(p.discount(10)) # 899.991
Dataclasses automatically generate __init__, __repr__, and __eq__ based on your type hints.
Callable Types
from typing import Callable
def apply(func: Callable[[int, int], int], a: int, b: int) -> int:
return func(a, b)
result = apply(lambda x, y: x + y, 5, 3)
print(result) # 8
Generic Types
from typing import TypeVar, Generic, List
T = TypeVar('T')
class Stack(Generic[T]):
def __init__(self) -> None:
self.items: List[T] = []
def push(self, item: T) -> None:
self.items.append(item)
def pop(self) -> T:
return self.items.pop()
stack: Stack[int] = Stack()
stack.push(1)
stack.push(2)
print(stack.pop()) # 2
Using mypy for Static Type Checking
Install mypy to catch type errors before runtime:
pip install mypy
def add(a: int, b: int) -> int:
return a + b
result = add("hello", 5) # Bug!
mypy buggy.py
# Error: Argument 1 to "add" has incompatible type "str"; expected "int"
Mypy catches the bug without running the code!
Real-World Example: Type-Hinted API Service
from typing import Optional
from dataclasses import dataclass
@dataclass
class User:
id: int
name: str
email: str
is_admin: bool = False
class UserService:
def __init__(self) -> None:
self.users: dict[int, User] = {}
def create(self, name: str, email: str) -> User:
new_id = len(self.users) + 1
user = User(id=new_id, name=name, email=email)
self.users[new_id] = user
return user
def find_by_id(self, user_id: int) -> Optional[User]:
return self.users.get(user_id)
def find_admins(self) -> list[User]:
return [u for u in self.users.values() if u.is_admin]
Conclusion
Type hints transform Python from a loosely-typed scripting language into a robust, self-documenting codebase. They enable better IDE autocomplete, catch bugs early with mypy, and are required by major frameworks like FastAPI and Pydantic. Start adding them to your code today — future you will thank you.