python-no-type-checking-guard

This skill defines hard rules and preferred patterns for writing typed Python code without ever using from typing import TYPE_CHECKING or if TYPE_CHECKING: blocks. It applies to all Python code the agent creates or edits unless the user explicitly overrides this style for a specific file.

jr2804/prompts1 installsMITSynced Aug 26

Works with

Claude CodeCursorCodex CLIGitHub CopilotGemini CLI

Agent Skills format with YAML frontmatter. Claude Code reads it as-is.

---
name: "python-no-type-checking-guard"
description: "This skill defines hard rules and preferred patterns for writing typed Python code without ever using from typing import TYPE_CHECKING or if TYPE_CHECKING: blocks. It applies to all Python code the agent creates or edits unless the user explicitly overrides this style for a specific file."
license: "MIT"
---

# Python typing without `TYPE_CHECKING`

This skill defines hard rules and preferred patterns for writing typed Python
code **without ever using** `from typing import TYPE_CHECKING` or
`if TYPE_CHECKING:` blocks. It applies to all Python code the agent creates
or edits unless the user explicitly overrides this style for a specific file.

For detailed examples and design rationale, consult the bundled reference
`references/python-module-design-without-type-checking.md`.

## Non‑negotiable rules

Follow these rules strictly unless the user explicitly instructs otherwise.

1. **Never import `TYPE_CHECKING`.**
   - Do **not** write `from typing import TYPE_CHECKING`.
   - Do **not** rely on `typing.TYPE_CHECKING` or any alias of it.
   - Do **not** introduce `TYPE_CHECKING = False` or similar "shadow"
     variables as an alternative.

2. **Never wrap imports in `if TYPE_CHECKING:`.**
   - Do **not** use `if TYPE_CHECKING:` blocks to guard imports, even if
     other style guides or examples recommend this pattern.
   - Do **not** move imports into `if TYPE_CHECKING:` solely to break
     circular imports or to "speed up" imports.

3. **Prefer normal, top‑level imports.**
   - Place imports at module top level in the usual way.
   - Only move imports inside functions/methods as a last resort for
     unavoidable runtime concerns (e.g. optional heavy dependencies),
     **not** for typing convenience.

4. **Do not increase divergence between runtime and type‑checking behavior.**
   - Generated code should behave the same under the Python interpreter and
     under static type checkers.
   - Avoid any pattern whose sole purpose is to hide imports from runtime
     while exposing them only to the type checker.

5. **Existing `TYPE_CHECKING` blocks: do not extend; prefer to remove.**
   - If a file already contains `TYPE_CHECKING` guards:
     - You **may leave them as‑is** when doing a minimal change and the
       user did not ask for structural refactoring.
     - **Do not add new guarded imports** or propagate this pattern into
       new modules.
     - When refactoring, prefer to replace such guards with cleaner
       designs described in this skill.

## How to avoid circular imports without `TYPE_CHECKING`

When tempted to use `TYPE_CHECKING` to "solve" a circular import, apply these
patterns instead. See the reference file for full examples and code snippets.

### 1. Extract shared types into a dedicated module

If two modules import each other only for types, extract the shared types
(`dataclass`, `Protocol`, `TypedDict`, enums, etc.) into a third module
(e.g. `models.py`, `types.py`, `domain.py`) and let both depend on that.
This keeps the dependency graph acyclic by design.

### 2. Depend on protocols or interfaces, not concrete classes

If several modules mutually depend on concrete implementations, introduce
`Protocol` or abstract base types in a separate `interfaces.py` / `ports.py`
module. Feature modules then depend only on the interface, breaking cycles
through dependency inversion.

### 3. Use forward references instead of guarded imports

Prefer string annotations (`"MyClass"`) or `from __future__ import annotations`
for forward references, rather than importing symbols into guarded blocks
purely for annotations. In newer Python versions, lazy evaluation of
annotations removes most needs for type‑only imports.

### 4. Local imports as a targeted workaround

Local imports inside functions are acceptable for **genuine runtime concerns**
(heavy optional dependencies, rarely‑used code paths, framework limitations),
but **never** solely for type hints. Document clearly _why_ the local import
exists.

## Handling existing code that uses `TYPE_CHECKING`

- **Minimal edits:** Leave existing guards intact. Do not introduce new
  guarded imports or extend the pattern into new modules.
- **Modernization / cleanup:** Identify why each `TYPE_CHECKING` block exists,
  then apply the patterns above (extract types, introduce protocols, use
  forward references) to gradually remove them. Re-run type checking and
  tests after each removal.

More General & Other skills

← All General & Other skills

Check your AI visibility

One URL in, a 0–100 score and the exact fixes out.

RUN THE CHECK

Browse all the tools

15 tools across six categories
13 of them never send your data anywhere

Free · No signup · No trial clock

SEE THE DIRECTORY