Skills · Coding

Python Type Safety

Unverified31/40

Python type safety with type hints, generics, protocols, and strict type checking. Use when adding type annotations, implementing generic classes, defining structural interfaces, or configuring mypy/pyright.

Originally by wshobson · MIT

Claude CodePartialHas SKILL.md but declares no allowed-tools — Claude Code will ask for permission each time
CursorPartialPlain prose you can paste in — but no Cursor rules file
CodexPartialPlain prose you can paste in — but no AGENTS.md
Gemini CLIPartialPlain prose you can paste in
CopilotPartialPlain prose you can paste in — but no Copilot instructions file
npx agentalley add python-type-safety

This command does not work yet — the CLI is still being built. Until then, use Raw in the reader below to take the file.

Who is stuck, and on what

Python type safety with type hints, generics, protocols, and strict type checking. Use when adding type annotations, implementing generic classes, defining structural interfaces, or configuring mypy/pyright.

The whole source

No sign-in, no blur, nothing truncated
python-type-safety/SKILL.md201 lines5.6 KBRawView on GitHub
Frontmatter — 2 properties
namepython-type-safety
descriptionPython type safety with type hints, generics, protocols, and strict type checking. Use when adding type annotations, implementing generic classes, defining structural interfaces, or configuring mypy/pyright.
1---
2name: python-type-safety
3description: Python type safety with type hints, generics, protocols, and strict type checking. Use when adding type annotations, implementing generic classes, defining structural interfaces, or configuring mypy/pyright.
4---A5No allowed-tools declared — no way to tell what this skill may touch
5 
6# Python Type Safety
7 
8Leverage Python's type system to catch errors at static analysis time. Type annotations serve as enforced documentation that tooling validates automatically.
9 
10## When to Use This Skill
11 
12- Adding type hints to existing code
13- Creating generic, reusable classes
14- Defining structural interfaces with protocols
15- Configuring mypy or pyright for strict checking
16- Understanding type narrowing and guards
17- Building type-safe APIs and libraries
18 
19## Core Concepts
20 
21### 1. Type Annotations
22 
23Declare expected types for function parameters, return values, and variables.
24 
25### 2. Generics
26 
27Write reusable code that preserves type information across different types.
28 
29### 3. Protocols
30 
31Define structural interfaces without inheritance (duck typing with type safety).
32 
33### 4. Type Narrowing
34 
35Use guards and conditionals to narrow types within code blocks.
36 
37## Quick Start
38 
39```python
40def get_user(user_id: str) -> User | None:
41 """Return type makes 'might not exist' explicit."""
42 ...
43 
44# Type checker enforces handling None case
45user = get_user("123")
46if user is None:
47 raise UserNotFoundError("123")
48print(user.name) # Type checker knows user is User here
49```
50 
51## Fundamental Patterns
52 
53### Pattern 1: Annotate All Public Signatures
54 
55Every public function, method, and class should have type annotations.
56 
57```python
58def get_user(user_id: str) -> User:
59 """Retrieve user by ID."""
60 ...
61 
62def process_batch(
63 items: list[Item],
64 max_workers: int = 4,
65) -> BatchResult[ProcessedItem]:
66 """Process items concurrently."""
67 ...
68 
69class UserRepository:
70 def __init__(self, db: Database) -> None:
71 self._db = db
72 
73 async def find_by_id(self, user_id: str) -> User | None:
74 """Return User if found, None otherwise."""
75 ...
76 
77 async def find_by_email(self, email: str) -> User | None:
78 ...
79 
80 async def save(self, user: User) -> User:
81 """Save and return user with generated ID."""
82 ...
83```
84 
85Use `mypy --strict` or `pyright` in CI to catch type errors early. For existing projects, enable strict mode incrementally using per-module overrides.
86 
87### Pattern 2: Use Modern Union Syntax
88 
89Python 3.10+ provides cleaner union syntax.
90 
91```python
92# Preferred (3.10+)
93def find_user(user_id: str) -> User | None:
94 ...
95 
96def parse_value(v: str) -> int | float | str:
97 ...
98 
99# Older style (still valid, needed for 3.9)
100from typing import Optional, Union
101 
102def find_user(user_id: str) -> Optional[User]:
103 ...
104```
105 
106### Pattern 3: Type Narrowing with Guards
107 
108Use conditionals to narrow types for the type checker.
109 
110```python
111def process_user(user_id: str) -> UserData:
112 user = find_user(user_id)
113 
114 if user is None:
115 raise UserNotFoundError(f"User {user_id} not found")
116 
117 # Type checker knows user is User here, not User | None
118 return UserData(
119 name=user.name,
120 email=user.email,
121 )
122 
123def process_items(items: list[Item | None]) -> list[ProcessedItem]:
124 # Filter and narrow types
125 valid_items = [item for item in items if item is not None]
126 # valid_items is now list[Item]
127 return [process(item) for item in valid_items]
128```
129 
130### Pattern 4: Generic Classes
131 
132Create type-safe reusable containers.
133 
134```python
135from typing import TypeVar, Generic
136 
137T = TypeVar("T")
138E = TypeVar("E", bound=Exception)
139 
140class Result(Generic[T, E]):
141 """Represents either a success value or an error."""
142 
143 def __init__(
144 self,
145 value: T | None = None,
146 error: E | None = None,
147 ) -> None:
148 if (value is None) == (error is None):
149 raise ValueError("Exactly one of value or error must be set")
150 self._value = value
151 self._error = error
152 
153 @property
154 def is_success(self) -> bool:
155 return self._error is None
156 
157 @property
158 def is_failure(self) -> bool:
159 return self._error is not None
160 
161 def unwrap(self) -> T:
162 """Get value or raise the error."""
163 if self._error is not None:
164 raise self._error
165 return self._value # type: ignore[return-value]
166 
167 def unwrap_or(self, default: T) -> T:
168 """Get value or return default."""
169 if self._error is not None:
170 return default
171 return self._value # type: ignore[return-value]
172 
173# Usage preserves types
174def parse_config(path: str) -> Result[Config, ConfigError]:
175 try:
176 return Result(value=Config.from_file(path))
177 except ConfigError as e:
178 return Result(error=e)
179 
180result = parse_config("config.yaml")
181if result.is_success:
182 config = result.unwrap() # Type: Config
183```
184 
185## Detailed worked examples and patterns
186 
187Detailed sections (starting with `## Advanced Patterns`) live in `references/details.md`. Read that file when the navigation summary above is insufficient.
188 
189## Best Practices Summary
190 
1911. **Annotate all public APIs** - Functions, methods, class attributes
1922. **Use `T | None`** - Modern union syntax over `Optional[T]`
1933. **Run strict type checking** - `mypy --strict` in CI
1944. **Use generics** - Preserve type info in reusable code
1955. **Define protocols** - Structural typing for interfaces
1966. **Narrow types** - Use guards to help the type checker
1977. **Bound type vars** - Restrict generics to meaningful types
1988. **Create type aliases** - Meaningful names for complex types
1999. **Minimize `Any`** - Use specific types or generics. `Any` is acceptable for truly dynamic data or when interfacing with untyped third-party code
20010. **Document with types** - Types are enforceable documentation
201 

Reviews

Installed this one?Write the first review and take the Trailblazer badge.

Reviews only open after a real install, so this is empty — and we leave it empty rather than invent one.

Alternatives

Also in Coding