# typing notes on type hints as actually used in projects like pdsx and fastmcp. ## unions use `|` for unions, not `Optional`: ```python RecordValue = str | int | float | bool | None | dict[str, Any] | list[Any] ``` from [pdsx/_internal/types.py](https://github.com/zzstoatzz/pdsx/blob/main/src/pdsx/_internal/types.py) ## TypedDict for structured dictionaries where you know the shape: ```python from typing import TypedDict class RecordResponse(TypedDict): """a record returned from list or get operations.""" uri: str cid: str | None value: dict class CredentialsContext(TypedDict): """credentials extracted from context or headers.""" handle: str | None password: str | None pds_url: str | None repo: str | None ``` better than `dict[str, Any]` because the structure is documented and checked. from [pdsx/mcp/_types.py](https://github.com/zzstoatzz/pdsx/blob/main/src/pdsx/mcp/_types.py) ## Annotated attach metadata to types. useful for documentation and schema generation: ```python from typing import Annotated from pydantic import Field FilterParam = Annotated[ str | None, Field( description=( "jmespath expression to filter/project the result. " "examples: '[*].{uri: uri, text: value.text}' (select fields), " "'[?value.text != null]' (filter items), " "'[*].uri' (extract values)" ), ), ] ``` the metadata travels with the type. MCP tools use this for parameter descriptions. from [pdsx/mcp/filterable.py](https://github.com/zzstoatzz/pdsx/blob/main/src/pdsx/mcp/filterable.py) ## Protocol define what methods something needs, not what class it is: ```python from typing import Protocol class ContextSamplingFallbackProtocol(Protocol): async def __call__( self, messages: str | list[str | SamplingMessage], system_prompt: str | None = None, temperature: float | None = None, max_tokens: int | None = None, ) -> ContentBlock: ... ``` any callable matching this signature satisfies the protocol. no inheritance required. from [fastmcp/utilities/types.py](https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/utilities/types.py) ## generics TypeVar for generic functions and classes: ```python from typing import ParamSpec, TypeVar P = ParamSpec("P") R = TypeVar("R") def filterable( fn: Callable[P, R] | Callable[P, Awaitable[R]], ) -> Callable[P, Any] | Callable[P, Awaitable[Any]]: ... ``` `ParamSpec` captures the full signature (args and kwargs) for decorator typing. from [pdsx/mcp/filterable.py](https://github.com/zzstoatzz/pdsx/blob/main/src/pdsx/mcp/filterable.py) ## ParamSpec for decorators that preserve function signatures: ```python @wraps(fn) async def async_wrapper( *args: P.args, _filter: str | None = None, **kwargs: P.kwargs ) -> Any: result = await fn(*args, **kwargs) return apply_filter(result, _filter) ``` `P.args` and `P.kwargs` carry the original function's parameter types into the wrapper. type checkers see the wrapper with the same signature as the wrapped function (plus any new parameters). from [pdsx/mcp/filterable.py](https://github.com/zzstoatzz/pdsx/blob/main/src/pdsx/mcp/filterable.py) ## overload when return type depends on input type: ```python from typing import overload @overload def filterable( fn: Callable[P, Awaitable[R]], ) -> Callable[P, Awaitable[Any]]: ... @overload def filterable( fn: Callable[P, R], ) -> Callable[P, Any]: ... def filterable( fn: Callable[P, R] | Callable[P, Awaitable[R]], ) -> Callable[P, Any] | Callable[P, Awaitable[Any]]: ... ``` type checkers know async functions get async wrappers, sync functions get sync wrappers. from [pdsx/mcp/filterable.py](https://github.com/zzstoatzz/pdsx/blob/main/src/pdsx/mcp/filterable.py) ## TYPE_CHECKING avoid runtime import costs for types only needed for hints: ```python from typing import TYPE_CHECKING if TYPE_CHECKING: from docket import Docket from docket.execution import Execution from fastmcp.tools.tool_transform import ArgTransform, TransformedTool ``` the import doesn't happen at runtime, only when type checkers analyze the code. with `from __future__ import annotations`, you don't need string quotes. from [fastmcp/tools/tool.py](https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/tools/tool.py) ## import organization ```python """module docstring.""" from __future__ import annotations import stdlib_module from typing import TYPE_CHECKING from third_party import thing from local_package import helper if TYPE_CHECKING: from expensive import Type ``` sources: - [pdsx/src/pdsx/_internal/types.py](https://github.com/zzstoatzz/pdsx/blob/main/src/pdsx/_internal/types.py) - [fastmcp/src/fastmcp/tools/tool.py](https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/tools/tool.py)