Skip to content
Sarj Code Standards

no-any-mapping-types

String-keyed mapping types must not erase their values with Any.

Why

An Any-valued mapping disables checking transitively and conceals whether the value is a fixed record, an arbitrary JSON tree, or a genuinely opaque Python object.

Fix

Use TypedDict (and Unpack[TypedDict] for closed keyword arguments) for fixed records, a Pydantic model for untrusted runtime input, pydantic.JsonValue for arbitrary JSON, or object plus explicit narrowing for a genuinely opaque Python value. Preserve unchanged external API signatures with an exact explained suppression; do not automatically widen published returns.

Examples

Before — flagged An Any-valued mapping erases the record schema
app/offers.py
from typing import Any
def details() -> dict[str, Any]: ...
After — preferred A TypedDict preserves the record schema
app/offers.py
from typing import TypedDict
class OfferDetails(TypedDict):
id: str
def details() -> OfferDetails: ...
Before — flagged An unrelated generic parameter preserves actual Any erasure
app/service.py
from typing import Any
class Rows[T]:
rows: dict[str, Any]
After — preferred A generic parameter named Any keeps its own identity
app/service.py
from typing import Any
class Rows[Any]:
rows: dict[str, Any]

Formerly: no-vague-annotations