| name | type-safety |
| description | Automatically applies when writing Python code to enforce comprehensive type hints. Ensures mypy compatibility, proper generic types, and type safety best practices. |
Type Safety Pattern Enforcer
All Python code should include comprehensive type hints for better IDE support, documentation, and error catching. This skill enforces type safety best practices and mypy compatibility.
✅ Correct Pattern
from decimal import Decimal
from typing import Any, Dict, List, Optional, Union, TypeVar, Generic
from collections.abc import Callable, Iterable
def process_transaction(
amount: Decimal,
user_id: str,
metadata: Optional[Dict[str, Any]] = None
) -> TransactionResult:
"""Process a transaction with validation."""
if metadata is None:
metadata = {}
return TransactionResult(
transaction_id="txn_123",
amount=amount,
status="completed"
)
class PaymentService:
"""Service for handling payments."""
api_key: str
base_url: str
timeout: int = 30
def __init__(self, api_key: str, base_url: str, timeout: int = 30) -> None:
"""Initialize payment service."""
self.api_key = api_key
self.base_url = base_url
self.timeout = timeout
async def fetch_user_data(user_id: str) -> UserData:
"""Fetch user data asynchronously."""
pass
Function Type Hints
def add(a: int, b: int) -> int:
"""Add two integers."""
return a + b
def get_user(user_id: str) -> Optional[User]:
"""Get user by ID, None if not found."""
pass
def log_event(event: str) -> None:
"""Log an event."""
print(event)
def concatenate(*args: str) -> str:
"""Concatenate strings."""
return "".join(args)
def create_user(**kwargs: Any) -> User:
"""Create user from keyword arguments."""
pass
def apply_operation(
value: int,
operation: Callable[[int], int]
) -> int:
"""Apply operation to value."""
return operation(value)
def process(value: int | str) -> str:
"""Process int or string."""
return str(value)
def process_legacy(value: Union[int, str]) -> str:
"""Process int or string."""
return str(value)
Class Type Hints
from typing import ClassVar
class User:
"""User with type-hinted attributes."""
id: str
name: str
email: str
age: Optional[int] = None
default_role: ClassVar[str] = "user"
def __init__(self, id: str, name: str, email: str) -> None:
"""Initialize user."""
self.id = id
self.name = name
self.email = email
def get_display_name(self) -> str:
"""Get display name."""
return self.name
@classmethod
def from_dict(cls, data: Dict[str, Any]) -> "User":
"""Create user from dictionary."""
return cls(
id=data["id"],
name=data["name"],
email=data["email"]
)
@staticmethod
def validate_email(email: str) -> bool:
"""Validate email format."""
return "@" in email
Generic Types
from typing import TypeVar, Generic, List, Dict
T = TypeVar('T')
K = TypeVar('K')
V = TypeVar('V')
class Container(Generic[T]):
"""Generic container for any type."""
def __init__(self, item: T) -> None:
"""Initialize with item."""
self.item = item
def get(self) -> T:
"""Get the item."""
return self.item
def set(self, item: T) -> None:
"""Set the item."""
self.item = item
class Cache(Generic[K, V]):
"""Generic key-value cache."""
def __init__(self) -> None:
"""Initialize cache."""
self._data: Dict[K, V] = {}
def get(self, key: K) -> Optional[V]:
"""Get value by key."""
return self._data.get(key)
def set(self, key: K, value: V) -> None:
"""Set key-value pair."""
self._data[key] = value
int_container: Container[int] = Container(42)
str_cache: Cache[str, User] = Cache()
Collection Type Hints
def process_items(items: list[str]) -> dict[str, int]:
"""Process list of strings."""
return {item: len(item) for item in items}
def aggregate(data: set[int]) -> tuple[int, int]:
"""Get min and max from set."""
return (min(data), max(data))
from typing import List, Dict, Set, Tuple
def process_items_legacy(items: List[str]) -> Dict[str, int]:
"""Process list of strings."""
return {item: len(item) for item in items}
def group_users(users: List[User]) -> Dict[str, List[User]]:
"""Group users by role."""
pass
from typing import Mapping, Sequence, Iterable
def process_mapping(data: Mapping[str, Any]) -> Sequence[str]:
"""Process read-only mapping."""
pass
def process_iterable(items: Iterable[int]) -> list[int]:
"""Process any iterable."""
return list(items)
Optional and None
def get_user(user_id: str) -> Optional[User]:
"""Get user, None if not found."""
pass
def process_user(user: Optional[User]) -> str:
"""Process user with None check."""
if user is None:
return "No user"
return user.name
def create_report(
title: str,
author: Optional[str] = None,
tags: Optional[List[str]] = None
) -> Report:
"""Create report with optional fields."""
if tags is None:
tags = []
return Report(title=title, author=author, tags=tags)
Async Type Hints
import asyncio
from typing import AsyncIterator
async def fetch_data(url: str) -> Dict[str, Any]:
"""Fetch data asynchronously."""
pass
async def fetch_all(urls: List[str]) -> List[Dict[str, Any]]:
"""Fetch multiple URLs."""
tasks = [fetch_data(url) for url in urls]
return await asyncio.gather(*tasks)
async def stream_data() -> AsyncIterator[str]:
"""Stream data asynchronously."""
for i in range(10):
await asyncio.sleep(0.1)
yield f"item-{i}"
async def main() -> None:
"""Main async function."""
data = await fetch_data("https://example.com")
async for item in stream_data():
print(item)
Type Narrowing
from typing import Union
def process_value(value: Union[int, str, None]) -> str:
"""Process value with type narrowing."""
if value is None:
return "none"
elif isinstance(value, int):
return f"int: {value * 2}"
elif isinstance(value, str):
return f"str: {value.upper()}"
else:
raise TypeError(f"Unexpected type: {type(value)}")
def process_user(user: Optional[User]) -> str:
"""Process user with None check."""
if user is None:
return "No user"
return user.name
Type Aliases
from typing import TypeAlias, NewType
UserId: TypeAlias = str
Email: TypeAlias = str
Metadata: TypeAlias = Dict[str, Any]
def get_user(user_id: UserId) -> User:
"""Get user by ID."""
pass
def send_email(email: Email, subject: str) -> None:
"""Send email."""
pass
UserId = NewType('UserId', str)
ProductId = NewType('ProductId', str)
def get_user(user_id: UserId) -> User:
"""Get user by ID."""
pass
user_id: UserId = UserId("usr_123")
product_id: ProductId = ProductId("prd_456")
Protocol Types
from typing import Protocol
class Drawable(Protocol):
"""Protocol for drawable objects."""
def draw(self) -> None:
"""Draw the object."""
...
class Circle:
"""Circle class."""
def draw(self) -> None:
"""Draw circle."""
print("Drawing circle")
class Square:
"""Square class."""
def draw(self) -> None:
"""Draw square."""
print("Drawing square")
def render(shape: Drawable) -> None:
"""Render any drawable shape."""
shape.draw()
render(Circle())
render(Square())
Literal Types
from typing import Literal
def set_status(status: Literal["active", "inactive", "suspended"]) -> None:
"""Set status to specific values only."""
pass
set_status("active")
def process_mode(mode: Literal["read", "write", "append"]) -> None:
"""Process with specific mode."""
if mode == "read":
pass
elif mode == "write":
pass
elif mode == "append":
pass
Type Guards
from typing import TypeGuard
def is_string_list(val: List[Any]) -> TypeGuard[List[str]]:
"""Check if list contains only strings."""
return all(isinstance(item, str) for item in val)
def process_strings(items: List[Any]) -> None:
"""Process list if all items are strings."""
if is_string_list(items):
for item in items:
print(item.upper())
❌ Anti-Patterns
def process_data(data):
return data
def process_data(data: Dict[str, Any]) -> Dict[str, Any]:
return data
def process(data: Any) -> Any:
pass
def process(data: Dict[str, str]) -> List[str]:
pass
def add_item(item: str, items: List[str] = []) -> List[str]:
items.append(item)
return items
def add_item(item: str, items: Optional[List[str]] = None) -> List[str]:
if items is None:
items = []
items.append(item)
return items
def get_user(user_id: str) -> User:
return None
def get_user(user_id: str) -> Optional[User]:
return None
def process(items: list) -> dict:
pass
from typing import List, Dict
def process(items: List[str]) -> Dict[str, int]:
pass
Best Practices Checklist
- ✅ All public functions have type hints
- ✅ All class attributes have type hints
- ✅ Use
Optional[T] for values that can be None
- ✅ Use specific types instead of
Any when possible
- ✅ Use modern syntax (
list[T]) on Python 3.9+
- ✅ Use
Protocol for structural typing
- ✅ Use
TypeAlias for complex type expressions
- ✅ Use
Literal for fixed string/int values
- ✅ Run mypy in strict mode
- ✅ Handle None cases explicitly
- ✅ Use type narrowing with isinstance checks
- ✅ Avoid mutable default arguments
Mypy Configuration
Add to pyproject.toml:
[tool.mypy]
python_version = "3.11"
strict = true
warn_return_any = true
warn_unused_configs = true
disallow_untyped_defs = true
disallow_any_generics = true
disallow_subclassing_any = true
disallow_untyped_calls = true
disallow_incomplete_defs = true
check_untyped_defs = true
disallow_untyped_decorators = true
no_implicit_optional = true
warn_redundant_casts = true
warn_unused_ignores = true
warn_no_return = true
Auto-Apply
When writing Python code:
- Add type hints to all function parameters
- Add return type hints to all functions
- Use
Optional[T] for nullable values
- Use specific collection types
- Run mypy to verify type safety
- Fix any type errors
References
For comprehensive examples, see:
Related Skills
- pydantic-models - For runtime type validation
- docstring-format - For documenting types
- async-await-checker - For async type hints