Now liveThe Skillselion MCP - thousands of ranked skills, loaded into your agent mid-task. No install.Get it →
josiahsiegel avatar

Python Type Hints

  • 55 installs
  • 50 repo stars
  • Updated June 18, 2026
  • josiahsiegel/claude-plugin-marketplace

Helps with python tasks.

About

python-type-hints is a Claude Code skill for python. It helps solo builders move faster with AI-assisted development.

  • python-type-hints
  • Python
  • AI-coding skill

Python Type Hints by the numbers

  • 55 all-time installs (skills.sh)
  • +5 installs in the week ending Aug 2, 2026 (Skillselion tracking)
  • Ranked #142 of 290 Python skills by installs in the Skillselion catalog
  • Data as of Aug 3, 2026 (Skillselion catalog sync)
npx skills add https://github.com/josiahsiegel/claude-plugin-marketplace --skill python-type-hints

Add your badge

Show developers this skill is listed on Skillselion. Paste this into your README.

Listed on Skillselion
Installs55
repo stars50
Last updatedJune 18, 2026
Repositoryjosiahsiegel/claude-plugin-marketplace

What it does

Helps with python tasks.

Files

SKILL.mdMarkdownGitHub ↗

Quick Reference

TypeSyntax (3.9+)Example
Listlist[str]names: list[str] = []
Dictdict[str, int]ages: dict[str, int] = {}
Optional`str \None`
Union`int \str`
CallableCallable[[int], str]func: Callable[[int], str]
FeatureVersionSyntax
Type params3.12+def first[T](items: list[T]) -> T:
type alias3.12+type Point = tuple[float, float]
Self3.11+def copy(self) -> Self:
TypeIs3.13+def is_str(x) -> TypeIs[str]:
ConstructUse Case
ProtocolStructural subtyping (duck typing)
TypedDictDict with specific keys
Literal["a", "b"]Specific values only
Final[str]Cannot be reassigned

When to Use This Skill

Use for static type checking:

  • Adding type hints to functions and classes
  • Creating typed dictionaries with TypedDict
  • Defining protocols for duck typing
  • Configuring mypy or pyright
  • Writing generic functions and classes

Related skills:

  • For Python fundamentals: see python-fundamentals-313
  • For testing: see python-testing
  • For FastAPI schemas: see python-fastapi

---

Python Type Hints Complete Guide

Overview

Type hints enable static type checking, better IDE support, and self-documenting code. Python's typing system is gradual - you can add types incrementally.

Modern Type Hints (Python 3.9+)

Built-in Generic Types

# Python 3.9+ - Use built-in types directly
# No need for typing.List, typing.Dict, etc.

def process_items(items: list[str]) -> dict[str, int]:
    return {item: len(item) for item in items}

# Collections
names: list[str] = ["Alice", "Bob"]
ages: dict[str, int] = {"Alice": 30, "Bob": 25}
coordinates: tuple[float, float] = (1.0, 2.0)
unique_ids: set[int] = {1, 2, 3}
frozen_data: frozenset[str] = frozenset(["a", "b"])

# Nested generics
matrix: list[list[int]] = [[1, 2], [3, 4]]
config: dict[str, list[str]] = {"servers": ["a", "b"]}

Union Types (Python 3.10+)

# Old way (still works)
from typing import Union, Optional

def old_style(value: Union[int, str]) -> Optional[str]:
    return str(value) if value else None

# New way (Python 3.10+)
def new_style(value: int | str) -> str | None:
    return str(value) if value else None

# Optional is just Union with None
# Optional[str] == str | None

Type Aliases

# Simple type alias
UserId = int
Username = str

def get_user(user_id: UserId) -> Username:
    return "user_" + str(user_id)

# Complex type alias
from typing import TypeAlias

JsonValue: TypeAlias = str | int | float | bool | None | list["JsonValue"] | dict[str, "JsonValue"]

# Python 3.12+ type statement
type Point = tuple[float, float]
type Vector[T] = list[T]
type JsonDict = dict[str, "JsonValue"]

Type Parameters (Python 3.12+)

# Old way with TypeVar
from typing import TypeVar

T = TypeVar("T")

def first_old(items: list[T]) -> T:
    return items[0]

# New way (Python 3.12+)
def first[T](items: list[T]) -> T:
    return items[0]

# Generic classes
class Stack[T]:
    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()

# Multiple type parameters
def merge[K, V](d1: dict[K, V], d2: dict[K, V]) -> dict[K, V]:
    return {**d1, **d2}

# Bounded type parameters
from typing import SupportsLessThan

def minimum[T: SupportsLessThan](a: T, b: T) -> T:
    return a if a < b else b

# Default type parameters (Python 3.13+)
class Container[T = int]:
    def __init__(self, value: T) -> None:
        self.value = value

Function Signatures

Basic Functions

from typing import Callable, Iterable, Iterator

# Simple function
def greet(name: str) -> str:
    return f"Hello, {name}!"

# Multiple parameters
def create_user(name: str, age: int, email: str | None = None) -> dict:
    return {"name": name, "age": age, "email": email}

# *args and **kwargs
def log(*args: str, **kwargs: int) -> None:
    for arg in args:
        print(arg)
    for key, value in kwargs.items():
        print(f"{key}={value}")

# Callable type
def apply_func(func: Callable[[int, int], int], a: int, b: int) -> int:
    return func(a, b)

# Higher-order functions
def make_multiplier(n: int) -> Callable[[int], int]:
    def multiplier(x: int) -> int:
        return x * n
    return multiplier

Overloads

from typing import overload, Literal

@overload
def process(data: str) -> str: ...
@overload
def process(data: bytes) -> bytes: ...
@overload
def process(data: int) -> int: ...

def process(data: str | bytes | int) -> str | bytes | int:
    if isinstance(data, str):
        return data.upper()
    elif isinstance(data, bytes):
        return data.upper()
    else:
        return data * 2

# Overload with Literal
@overload
def fetch(url: str, format: Literal["json"]) -> dict: ...
@overload
def fetch(url: str, format: Literal["text"]) -> str: ...
@overload
def fetch(url: str, format: Literal["bytes"]) -> bytes: ...

def fetch(url: str, format: str) -> dict | str | bytes:
    # Implementation
    ...

ParamSpec for Decorators

from typing import ParamSpec, TypeVar, Callable
from functools import wraps

P = ParamSpec("P")
R = TypeVar("R")

def log_calls(func: Callable[P, R]) -> Callable[P, R]:
    @wraps(func)
    def wrapper(*args: P.args, **kwargs: P.kwargs) -> R:
        print(f"Calling {func.__name__}")
        return func(*args, **kwargs)
    return wrapper

@log_calls
def add(a: int, b: int) -> int:
    return a + b

# Python 3.12+ syntax
def log_calls_new[**P, R](func: Callable[P, R]) -> Callable[P, R]:
    @wraps(func)
    def wrapper(*args: P.args, **kwargs: P.kwargs) -> R:
        print(f"Calling {func.__name__}")
        return func(*args, **kwargs)
    return wrapper

Classes and Protocols

Class Typing

from typing import ClassVar, Self

class User:
    # Class variable
    count: ClassVar[int] = 0

    def __init__(self, name: str, age: int) -> None:
        self.name = name
        self.age = age
        User.count += 1

    # Self type for method chaining
    def with_name(self, name: str) -> Self:
        self.name = name
        return self

    def with_age(self, age: int) -> Self:
        self.age = age
        return self

# Usage
user = User("Alice", 30).with_name("Bob").with_age(25)

Protocols (Structural Subtyping)

from typing import Protocol, runtime_checkable

# Define a protocol (interface)
class Drawable(Protocol):
    def draw(self) -> None: ...

class Resizable(Protocol):
    def resize(self, width: int, height: int) -> None: ...

# Combining protocols
class DrawableAndResizable(Drawable, Resizable, Protocol):
    pass

# Implementation (no explicit inheritance needed!)
class Circle:
    def draw(self) -> None:
        print("Drawing circle")

class Rectangle:
    def draw(self) -> None:
        print("Drawing rectangle")

    def resize(self, width: int, height: int) -> None:
        print(f"Resizing to {width}x{height}")

# Works because Circle has draw() method
def render(shape: Drawable) -> None:
    shape.draw()

render(Circle())  # OK - Circle satisfies Drawable protocol

# Runtime checkable protocol
@runtime_checkable
class Closeable(Protocol):
    def close(self) -> None: ...

# Can use isinstance
if isinstance(file, Closeable):
    file.close()

TypedDict

from typing import TypedDict, Required, NotRequired

# Basic TypedDict
class Movie(TypedDict):
    title: str
    year: int
    director: str

movie: Movie = {"title": "Inception", "year": 2010, "director": "Nolan"}

# With optional keys
class UserProfile(TypedDict, total=False):
    name: str  # Optional
    email: str  # Optional
    age: int   # Optional

# Mixed required and optional (Python 3.11+)
class Article(TypedDict):
    title: Required[str]
    content: Required[str]
    author: NotRequired[str]
    tags: NotRequired[list[str]]

# Inheritance
class DetailedMovie(Movie):
    rating: float
    genres: list[str]

Abstract Base Classes

from abc import ABC, abstractmethod

class Repository[T](ABC):
    @abstractmethod
    def get(self, id: int) -> T | None:
        ...

    @abstractmethod
    def save(self, entity: T) -> T:
        ...

    @abstractmethod
    def delete(self, id: int) -> bool:
        ...

class UserRepository(Repository["User"]):
    def get(self, id: int) -> "User | None":
        return self._db.get(id)

    def save(self, entity: "User") -> "User":
        return self._db.save(entity)

    def delete(self, id: int) -> bool:
        return self._db.delete(id)

Advanced Types

Literal Types

from typing import Literal

# Restrict to specific values
Mode = Literal["r", "w", "a", "rb", "wb"]

def open_file(path: str, mode: Mode) -> None:
    ...

open_file("test.txt", "r")   # OK
open_file("test.txt", "x")   # Type error!

# Combining literals
HttpMethod = Literal["GET", "POST", "PUT", "DELETE", "PATCH"]
StatusCode = Literal[200, 201, 400, 401, 403, 404, 500]

Final and Const

from typing import Final

# Constant that shouldn't be reassigned
MAX_SIZE: Final = 100
API_URL: Final[str] = "https://api.example.com"

# Final class methods
class Base:
    from typing import final

    @final
    def critical_method(self) -> None:
        """Cannot be overridden in subclasses."""
        ...

# Final classes
from typing import final

@final
class Singleton:
    """Cannot be subclassed."""
    _instance: "Singleton | None" = None

Type Guards

from typing import TypeGuard, TypeIs

# TypeGuard (narrows type in if block)
def is_string_list(val: list[object]) -> TypeGuard[list[str]]:
    return all(isinstance(x, str) for x in val)

def process(items: list[object]) -> None:
    if is_string_list(items):
        # items is now list[str]
        for item in items:
            print(item.upper())

# TypeIs (Python 3.13+ - stricter than TypeGuard)
def is_int(val: int | str) -> TypeIs[int]:
    return isinstance(val, int)

def handle(value: int | str) -> None:
    if is_int(value):
        # value is int
        print(value + 1)
    else:
        # value is str (properly narrowed)
        print(value.upper())

Annotated

from typing import Annotated
from dataclasses import dataclass

# Metadata for validation/documentation
UserId = Annotated[int, "Unique user identifier"]
Email = Annotated[str, "Valid email address"]
Age = Annotated[int, "Must be >= 0"]

@dataclass
class User:
    id: UserId
    email: Email
    age: Age

# With Pydantic
from pydantic import BaseModel, Field

class UserModel(BaseModel):
    id: Annotated[int, Field(gt=0)]
    email: Annotated[str, Field(pattern=r"^[\w.-]+@[\w.-]+\.\w+$")]
    age: Annotated[int, Field(ge=0, le=150)]

Type Checking Tools

Mypy Configuration

# pyproject.toml
[tool.mypy]
python_version = "3.12"
strict = true
warn_return_any = true
warn_unused_ignores = true
disallow_untyped_defs = true
disallow_incomplete_defs = true
check_untyped_defs = true
disallow_untyped_decorators = true
no_implicit_optional = true
warn_redundant_casts = true
warn_unused_configs = true

# Per-module overrides
[[tool.mypy.overrides]]
module = "tests.*"
disallow_untyped_defs = false

[[tool.mypy.overrides]]
module = "third_party.*"
ignore_missing_imports = true

Pyright Configuration

// pyrightconfig.json
{
  "include": ["src"],
  "exclude": ["**/node_modules", "**/__pycache__"],
  "typeCheckingMode": "strict",
  "pythonVersion": "3.12",
  "reportMissingImports": true,
  "reportMissingTypeStubs": false,
  "reportUnusedImport": true,
  "reportUnusedVariable": true
}

Running Type Checkers

# Mypy
mypy src/ --strict
mypy src/ --ignore-missing-imports

# Pyright (faster, VS Code default)
pyright src/

# With uv
uv run mypy src/

Best Practices

1. Use Native Generics (3.9+)

# Preferred (Python 3.9+)
items: list[str] = []
mapping: dict[str, int] = {}

# Avoid (old style)
from typing import List, Dict
items: List[str] = []  # Deprecated

2. Prefer Protocols Over ABCs

# Preferred - structural typing
from typing import Protocol

class Serializable(Protocol):
    def to_json(self) -> str: ...

# Less flexible - nominal typing
from abc import ABC, abstractmethod

class SerializableABC(ABC):
    @abstractmethod
    def to_json(self) -> str: ...

3. Use Abstract Collection Types

from collections.abc import Iterable, Sequence, Mapping, MutableMapping

# Prefer abstract types for function parameters
def process_items(items: Iterable[str]) -> list[str]:
    return [item.upper() for item in items]

def lookup(data: Mapping[str, int], key: str) -> int | None:
    return data.get(key)

# Works with any iterable/mapping
process_items(["a", "b"])      # list
process_items({"a", "b"})      # set
process_items(("a", "b"))      # tuple
process_items(x for x in "ab") # generator

4. Gradual Typing Strategy

# Start with public API
def public_function(data: dict[str, Any]) -> list[str]:
    return _internal_helper(data)

# Type internal helpers later
def _internal_helper(data):  # Untyped initially
    ...

# Aim for 80%+ coverage on new code
# Use # type: ignore sparingly

5. Document Complex Types

from typing import TypeAlias

# Use type aliases for complex types
JsonPrimitive: TypeAlias = str | int | float | bool | None
JsonArray: TypeAlias = list["JsonValue"]
JsonObject: TypeAlias = dict[str, "JsonValue"]
JsonValue: TypeAlias = JsonPrimitive | JsonArray | JsonObject

def parse_json(text: str) -> JsonValue:
    """Parse JSON string into typed Python value."""
    import json
    return json.loads(text)

Additional References

For advanced typing patterns beyond this guide, see:

  • [Advanced Typing Patterns](references/advanced-typing-patterns.md) - Generic repository pattern, discriminated unions, builder pattern with Self, ParamSpec decorators, conditional types with overloads, typed decorator factories, Protocols with class methods, typed context variables, recursive types, typed event systems

Related skills

Pythonbackend

This week in AI coding

Five minutes, every Monday - the tools, releases and tactics for developers.

unsubscribe anytime.