.agents/skills/dignified-python/references/advanced/typing-advanced.md
Read when: Using typing.cast(), creating Literal type aliases, narrowing types
typing.cast()ALWAYS verify cast() with a runtime assertion, unless there's a documented reason not to.
typing.cast() is a compile-time only construct—it tells the type checker to trust you but performs
no runtime verification. If your assumption is wrong, you'll get silent misbehavior instead of a
clear error.
from collections.abc import MutableMapping
from typing import Any, cast
# CORRECT: Runtime assertion before cast
assert isinstance(doc, MutableMapping), f"Expected MutableMapping, got {type(doc)}"
cast(dict[str, Any], doc)["key"] = value
# CORRECT: Alternative with hasattr for duck typing
assert hasattr(obj, '__setitem__'), f"Expected subscriptable, got {type(obj)}"
cast(dict[str, Any], obj)["key"] = value
# WRONG: Cast without runtime verification
cast(dict[str, Any], doc)["key"] = value # If doc isn't a dict-like, silent failure
Default: Always add the assertion when cost is trivial (O(1) checks like in, isinstance).
Skip the assertion only in these narrow cases:
Immediately after a type guard: The check was just performed and would be redundant
if isinstance(value, str):
# No assertion needed - we just checked
result = cast(str, value).upper()
Performance-critical hot path: Add a comment explaining the measured overhead
# Skip assertion: called 10M times/sec, isinstance adds 15% overhead
# Type invariant maintained by _validate_input() at entry point
cast(int, cached_value)
What is NOT a valid reason to skip:
Use Literal types for strings that have programmatic meaning.
When strings represent a fixed set of valid values (error codes, status values, command types),
model them in the type system using Literal.
Use kebab-case for all internal Literal string values:
# CORRECT: kebab-case for internal values
IssueCode = Literal["orphan-state", "orphan-dir", "missing-branch"]
ErrorType = Literal["not-found", "invalid-format", "timeout-exceeded"]
Exception: When modeling external systems, match the external API's convention:
# CORRECT: Match GitHub API's UPPER_CASE
PRState = Literal["OPEN", "MERGED", "CLOSED"]
# CORRECT: Match GitHub Actions API's lowercase
WorkflowStatus = Literal["completed", "in_progress", "queued"]
The rule is: kebab-case by default, external convention when modeling external APIs.
from dataclasses import dataclass
from typing import Literal
# CORRECT: Define a type alias for the valid values
IssueCode = Literal["orphan-state", "orphan-dir", "missing-branch"]
@dataclass(frozen=True)
class Issue:
code: IssueCode
message: str
def check_state() -> list[Issue]:
issues: list[Issue] = []
if problem_detected:
issues.append(Issue(code="orphan-state", message="description")) # Type-checked!
return issues
# WRONG: Bare strings without type constraint
def check_state() -> list[tuple[str, str]]:
issues: list[tuple[str, str]] = []
issues.append(("orphen-state", "desc")) # Typo goes unnoticed!
return issues
Before using a bare str type, ask:
== or in anywhere?If any answer is "yes", use Literal instead.