Python’s typing module provides type annotation building blocks for static analysis with mypy/pyright. from typing import Any, Callable, ClassVar, Final, Generic, Literal, NamedTuple, Optional, Protocol, TypeVar, Union, overload, cast, TYPE_CHECKING. TypeVar: T = TypeVar("T"); K = TypeVar("K", bound=Hashable). Generic: class Stack(Generic[T]): def push(self, item: T) -> None. Protocol: class Sized(Protocol): def __len__(self) -> int — structural subtyping. TypedDict: class Config(TypedDict): host: str; port: int. Literal: def set_level(level: Literal["DEBUG","INFO","WARNING"]) -> None. Union: str | int (Python 3.10+) or Union[str, int]. Optional: Optional[str] == str | None. overload: @overload def fn(x: int) -> int: ...; @overload def fn(x: str) -> str: .... cast: x = cast(int, some_value) — type-checker hint only, no runtime effect. TYPE_CHECKING: if TYPE_CHECKING: from heavy_module import HeavyType. Final: MAX: Final = 100 — immutable. ClassVar: class C: count: ClassVar[int] = 0 — not in instances. Annotated: Height = Annotated[float, Gt(0)]. ParamSpec: P = ParamSpec("P") — preserve callable signature in decorators. TypeGuard: def is_str(val: object) -> TypeGuard[str]: return isinstance(val, str). Self: def copy(self) -> Self (Python 3.11+). get_type_hints: typing.get_type_hints(cls) — resolved annotations dict. Claude Code generates generic containers, Protocol interfaces, decorator signatures, and TypedDict schemas.
CLAUDE.md for typing
## typing Stack
- Stdlib: from typing import TypeVar, Generic, Protocol, TypedDict, Literal, overload, TYPE_CHECKING
- TypeVar: T = TypeVar("T") | K = TypeVar("K", bound=Hashable) | use in Generic[T] and -> T
- Protocol: structural interface — no inheritance needed; use @runtime_checkable for isinstance
- Overload: @overload stubs + un-decorated impl — one per distinct signature
- Self: -> Self for fluent interfaces (Python 3.11+; use TypeVar T for 3.10)
- TYPE_CHECKING: guard heavy imports used only for annotations
typing Annotation Pipeline
# app/typed.py — TypeVar, Generic, Protocol, TypedDict, Literal, overload, ParamSpec
from __future__ import annotations
import copy
import functools
import sys
from typing import (
TYPE_CHECKING,
Annotated,
Any,
Callable,
ClassVar,
Final,
Generic,
Hashable,
Iterator,
Literal,
NamedTuple,
Optional,
Protocol,
Sequence,
TypeVar,
Union,
cast,
get_type_hints,
overload,
runtime_checkable,
)
if sys.version_info >= (3, 10):
from typing import ParamSpec, TypeGuard
else:
from typing import TypeVar as ParamSpec # fallback (no-op typing)
TypeGuard = Any # type: ignore
if TYPE_CHECKING:
pass # place heavy, annotation-only imports here
# ─────────────────────────────────────────────────────────────────────────────
# 1. TypeVar + Generic containers
# ─────────────────────────────────────────────────────────────────────────────
T = TypeVar("T")
K = TypeVar("K", bound=Hashable)
V = TypeVar("V")
T_co = TypeVar("T_co", covariant=True)
class Stack(Generic[T]):
"""
Generic LIFO stack.
Example:
s: Stack[int] = Stack()
s.push(1)
s.push(2)
top = s.pop() # 2
"""
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()
def peek(self) -> T:
return self._items[-1]
def __len__(self) -> int:
return len(self._items)
def __bool__(self) -> bool:
return bool(self._items)
def __iter__(self) -> Iterator[T]:
return reversed(self._items)
class Result(Generic[T]):
"""
Typed result that holds either a success value or an error.
Example:
ok: Result[int] = Result.ok(42)
err: Result[int] = Result.err("not found")
if ok.is_ok:
print(ok.value)
"""
def __init__(self, value: T | None, error: str | None) -> None:
self._value = value
self._error = error
@classmethod
def ok(cls, value: T) -> Result[T]:
return cls(value, None)
@classmethod
def err(cls, error: str) -> Result[T]:
return cls(None, error)
@property
def is_ok(self) -> bool:
return self._error is None
@property
def value(self) -> T:
if self._error is not None:
raise ValueError(self._error)
return cast(T, self._value)
@property
def error(self) -> str | None:
return self._error
def map(self, fn: Callable[[T], V]) -> Result[V]:
if self._error:
return Result.err(self._error) # type: ignore[return-value]
return Result.ok(fn(cast(T, self._value)))
def __repr__(self) -> str:
if self.is_ok:
return f"Ok({self._value!r})"
return f"Err({self._error!r})"
# ─────────────────────────────────────────────────────────────────────────────
# 2. Protocol — structural subtyping
# ─────────────────────────────────────────────────────────────────────────────
@runtime_checkable
class Serializable(Protocol):
"""
Any object that can serialize itself to a dict.
Example:
def save(obj: Serializable) -> None:
json.dump(obj.to_dict(), f)
"""
def to_dict(self) -> dict[str, Any]: ...
@runtime_checkable
class Comparable(Protocol[T_co]):
"""Structural protocol for objects supporting < and ==."""
def __lt__(self, other: Any) -> bool: ...
def __eq__(self, other: object) -> bool: ...
@runtime_checkable
class Closeable(Protocol):
"""Structural protocol for objects with a .close() method."""
def close(self) -> None: ...
def process_serializable(obj: Serializable) -> str:
"""
Accept any object matching the Serializable protocol.
Example:
class User:
def to_dict(self): return {"id": self.id, "name": self.name}
process_serializable(User(id=1, name="Alice")) # works — no inheritance needed
"""
import json
return json.dumps(obj.to_dict())
# ─────────────────────────────────────────────────────────────────────────────
# 3. TypedDict schemas
# ─────────────────────────────────────────────────────────────────────────────
from typing import TypedDict # noqa: E402
class UserConfig(TypedDict):
"""Typed dict for user configuration."""
host: str
port: int
debug: bool
class UserConfigOptional(TypedDict, total=False):
"""TypedDict with all-optional keys."""
timeout: float
max_retry: int
log_level: str
class DatabaseConfig(TypedDict):
"""Nested TypedDict."""
url: str
pool_size: int
options: UserConfigOptional
class APIResponse(TypedDict):
"""Typed API response envelope."""
success: bool
data: Any
error: Optional[str]
meta: dict[str, Any]
# ─────────────────────────────────────────────────────────────────────────────
# 4. Literal types
# ─────────────────────────────────────────────────────────────────────────────
LogLevel = Literal["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"]
HTTPVerb = Literal["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"]
SortOrder = Literal["asc", "desc"]
OutputFmt = Literal["json", "csv", "parquet", "arrow"]
def set_log_level(level: LogLevel) -> None:
"""
Only accepts valid log level strings — caught at type-check time.
Example:
set_log_level("INFO") # OK
set_log_level("TRACE") # mypy/pyright error
"""
import logging
logging.getLogger().setLevel(level)
def make_request(
verb: HTTPVerb,
url: str,
*,
order: SortOrder = "asc",
fmt: OutputFmt = "json",
) -> dict:
return {"verb": verb, "url": url, "order": order, "fmt": fmt}
# ─────────────────────────────────────────────────────────────────────────────
# 5. overload — multiple return types
# ─────────────────────────────────────────────────────────────────────────────
@overload
def parse(value: str) -> int: ...
@overload
def parse(value: bytes) -> str: ...
@overload
def parse(value: int) -> float: ...
def parse(value: str | bytes | int) -> int | str | float:
"""
Typed multi-dispatch: return type depends on input type.
Example:
x: int = parse("42") # str → int
y: str = parse(b"hello") # bytes → str
z: float = parse(7) # int → float
"""
if isinstance(value, str):
return int(value)
if isinstance(value, bytes):
return value.decode()
return float(value)
# ─────────────────────────────────────────────────────────────────────────────
# 6. TypeGuard + type narrowing
# ─────────────────────────────────────────────────────────────────────────────
def is_str_list(val: list[Any]) -> TypeGuard: # type: ignore[type-arg]
"""
Narrow list[Any] to list[str] after this guard returns True.
Example:
data: list[Any] = get_raw()
if is_str_list(data):
joined: str = ", ".join(data) # type-safe
"""
return all(isinstance(item, str) for item in val)
def is_int(val: object) -> bool:
"""isinstance guard usable in type-narrowing branches."""
return isinstance(val, int)
# ─────────────────────────────────────────────────────────────────────────────
# 7. Decorator with ParamSpec
# ─────────────────────────────────────────────────────────────────────────────
if sys.version_info >= (3, 10):
from typing import ParamSpec as _PS
_P = _PS("_P")
else:
_P = TypeVar("_P") # type: ignore[assignment]
def logged(fn: Callable) -> Callable:
"""
Decorator that preserves the wrapped function's full signature.
With ParamSpec this is fully type-safe on Python 3.10+.
Example:
@logged
def add(x: int, y: int) -> int:
return x + y
add(1, 2) # type checker knows return=int and args=(int, int)
"""
@functools.wraps(fn)
def wrapper(*args, **kwargs):
import logging
logging.getLogger(fn.__module__).debug("call %s", fn.__name__)
result = fn(*args, **kwargs)
logging.getLogger(fn.__module__).debug("done %s", fn.__name__)
return result
return wrapper
# ─────────────────────────────────────────────────────────────────────────────
# 8. Final + ClassVar
# ─────────────────────────────────────────────────────────────────────────────
MAX_RETRIES: Final[int] = 3
DEFAULT_TIMEOUT: Final[float] = 30.0
API_VERSION: Final = "v2"
class AppConfig:
_instances: ClassVar[dict[str, AppConfig]] = {}
_registry: ClassVar[list[str]] = []
def __init__(self, name: str, debug: bool = False) -> None:
self.name = name
self.debug = debug
AppConfig._registry.append(name)
# ─────────────────────────────────────────────────────────────────────────────
# Demo
# ─────────────────────────────────────────────────────────────────────────────
if __name__ == "__main__":
import json
print("=== typing demo ===")
print("\n--- Generic Stack[int] ---")
s: Stack[int] = Stack()
for i in [1, 2, 3]:
s.push(i)
print(f" len={len(s)} top={s.peek()} pop={s.pop()}")
print("\n--- Result[int] ---")
r = Result.ok(42).map(lambda x: x * 2)
print(f" ok.map(*2): {r}")
r2 = Result.err("not found")
print(f" err: {r2} is_ok={r2.is_ok} error={r2.error!r}")
print("\n--- Protocol (Serializable) ---")
class Point:
def __init__(self, x, y):
self.x, self.y = x, y
def to_dict(self):
return {"x": self.x, "y": self.y}
p = Point(3, 4)
print(f" isinstance(p, Serializable): {isinstance(p, Serializable)}")
print(f" process_serializable: {process_serializable(p)}")
print("\n--- TypedDict ---")
cfg: UserConfig = {"host": "localhost", "port": 8080, "debug": True}
print(f" cfg: {cfg}")
print("\n--- Literal ---")
req = make_request("GET", "https://api.example.com/data", order="desc", fmt="parquet")
print(f" req: {req}")
print("\n--- overload parse ---")
print(f" parse('42') = {parse('42')!r}")
print(f" parse(b'hello') = {parse(b'hello')!r}")
print(f" parse(7) = {parse(7)!r}")
print("\n--- TypeGuard ---")
data: list[Any] = ["a", "b", "c"]
print(f" is_str_list(['a','b','c']): {is_str_list(data)}")
print(f" is_str_list([1,2,3]): {is_str_list([1,2,3])}")
print("\n--- Final + ClassVar ---")
print(f" MAX_RETRIES={MAX_RETRIES} API_VERSION={API_VERSION!r}")
AppConfig("app1")
AppConfig("app2")
print(f" AppConfig._registry: {AppConfig._registry}")
print("\n--- get_type_hints ---")
hints = get_type_hints(UserConfig)
print(f" UserConfig hints: {hints}")
print("\n=== done ===")
For the mypy alternative — mypy is the canonical static type checker for Python that reads type annotations and reports type errors before runtime; typing provides just the annotation building blocks — both are used together: write annotations with typing, validate them with mypy --strict; pyright (Microsoft) is a faster alternative checker with better LSP integration that powers Pylance in VS Code. For the beartype alternative — beartype (PyPI) provides runtime type checking by validating actual call arguments against type annotations at O(1) per call, catching type errors that static checkers cannot see (e.g. data from external APIs); typing annotations alone are ignored at runtime by default — use @beartype decorator or BeartypeConf when you need runtime enforcement at API boundaries, typing alone for static analysis with mypy/pyright in CI. The Claude Skills 360 bundle includes typing skill sets covering Stack/Result Generic containers, Serializable/Comparable/Closeable Protocol interfaces, UserConfig/DatabaseConfig/APIResponse TypedDict schemas, LogLevel/HTTPVerb/SortOrder Literal types, overload parse() multi-signature, is_str_list() TypeGuard narrowing, logged() decorator with ParamSpec, and Final/ClassVar class-level constants. Start with the free tier to try gradual static typing and typing annotation pipeline code generation.