| name | pythonista-typing |
| description | Use when adding type hints, fixing type checker errors, working with Pydantic models. Triggers on "type", "types", "typing", "Pydantic", "BaseModel", "pyright", "basedpyright", "mypy", "type error", "type hint", "annotation", "Any", "dict", "list", "Optional", "Cannot assign", "Incompatible types", "Missing return type", "reportArgumentType", or when defining function signatures or data models. |
Type Annotations and Pydantic Best Practices
Core Philosophy
Use Pydantic models for structured data. Use specific types everywhere. Never use Any or raw dicts when structure is known.
Quick Start - Fixing Type Errors
pyright <file-or-directory>
basedpyright <file-or-directory>
pytest tests/
Fix Priority Order
- Add proper type annotations (Optional, specific types)
- Fix decorator return types
- Use
cast() for runtime-compatible but statically unverifiable types
- Last resort:
# type: ignore only for legitimate cases
Type Annotation Rules
Modern Python Syntax (3.9+)
def process_data(items: list[str]) -> dict[str, list[int]]:
results: dict[str, list[int]] = {}
return results
from typing import Dict, List
def process_data(items: List[str]) -> Dict[str, List[int]]:
...
NEVER Use Weak Types
items: list[Any]
data: dict
result: Any
items: list[DataItem]
data: dict[str, ProcessedResult]
result: SpecificType | OtherType
NEVER Use hasattr/getattr as Type Substitutes
def process(obj: Any):
if hasattr(obj, "name"):
return obj.name
class Named(Protocol):
name: str
def process(obj: Named) -> str:
return obj.name
Complex Return Types Must Be Named
def execute() -> tuple[BatchResults, dict[str, Optional[Egress]]]:
pass
class ExecutionResult(BaseModel):
batch_results: BatchResults
egress_statuses: dict[str, Optional[Egress]]
def execute() -> ExecutionResult:
pass
Rule: If you can't read the type annotation out loud in one breath, it needs a named model.
Pydantic Rules
Always Use Pydantic Models for Structured Data
def get_result() -> dict[str, Any]:
return {"is_valid": True, "score": 0.95}
class Result(BaseModel):
is_valid: bool
score: float
def get_result() -> Result:
return Result(is_valid=True, score=0.95)
TypedDict and dataclasses Are Prohibited
NEVER use TypedDict or dataclasses without explicit authorization. Always use Pydantic.
Never Convert Models to Dicts Just to Add Fields
result_dict = result.model_dump()
result_dict["_run_id"] = run_id
class ResultWithRunId(BaseModel):
details: ResultDetails
run_id: str | None = None
Prefer cast() Over type: ignore
from typing import cast
typed_results = cast(list[ResultProtocol], results)
selected = select_by_score(typed_results)
selected = select_by_score(results)
When to Use type: ignore
Only for:
- Function attributes:
func._attr = val # type: ignore[attr-defined]
- Dynamic/runtime attributes not in type system
- External library quirks (protobuf, webhooks)
- Legacy patterns requiring significant refactoring
DO NOT use for simple fixes (add Optional, fix return types).
Reference Files
For detailed patterns:
Related Skills