The essentials

Quick reference

One focused task per row. Jump to the related section for complete, working examples.

UseSyntaxExamples
Annotate a functiondef total(values: list[float]) -> float: ...View examples
No return valuedef log(message: str) -> None: ...View examples
Union typeidentifier: int | strView examples
Optional valuename: str | None = NoneView examples
Narrow by runtime typeif isinstance(value, str): return value.casefold()View examples
Read-only collection inputdef first(values: Sequence[str]) -> str: ...View examples
Read-only mapping inputdef render(values: Mapping[str, object]) -> str: ...View examples
Callable signaturetransform: Callable[[str], str]View examples
Declare a type aliastype UserId = intView examples
Typed dictionary shapeclass UserRow(TypedDict): ...View examples
Generic type parameterdef first[T](values: Sequence[T]) -> T: ...View examples
Structural protocolclass SupportsClose(Protocol): ...View examples
Runtime protocol check@runtime_checkableView examples
User-defined narrowingdef is_str_list(value: list[object]) -> TypeGuard[list[str]]: ...View examples

Type hints describe expected values for static analyzers, IDEs, and readers; Python normally does not enforce them at runtime. Annotate public boundaries and domain concepts, prefer precise structural interfaces over broad object types, and keep annotations maintainable enough that the checker remains trusted.

Step by step

Detailed examples

01

Annotate contracts, not implementation trivia

Annotations are stored for introspection and consumed by external tools; calls still accept arbitrary values unless code or a framework validates them. Use None for procedures, avoid Any where a real interface exists, and annotate public functions before local variables whose types are already obvious.

Typed calculation remains ordinary Python
def mean(values: list[float]) -> float:
    if not values:
        raise ValueError('values cannot be empty')
    return sum(values) / len(values)

print(mean([2.0, 4.0, 6.0]))
print(mean.__annotations__)
Output
4.0
{'values': list[float], 'return': <class 'float'>}
Back to quick reference ↑
02

Narrow alternatives before using specific operations

A union permits only operations valid for every member until control flow narrows it. isinstance, identity comparison with None, literal comparisons, match patterns, and TypeGuard functions can narrow. Do not use casts to silence evidence of a real unchecked boundary; validate external data first.

Narrow a union
def normalize(value: int | str | None) -> str:
    if value is None:
        return 'missing'
    if isinstance(value, int):
        return str(value)
    return value.casefold()

print([normalize(value) for value in [42, 'READY', None]])
Output
['42', 'ready', 'missing']
Back to quick reference ↑
03

Accept abstract read-only interfaces when mutation is unnecessary

Sequence and Mapping widen callers while documenting supported operations. Mutable containers are invariant in common checkers because writing a broader type could violate a narrower collection. Callable describes argument and result types; Protocol is better when callbacks have named attributes or overloaded call forms.

Compose through abstract inputs
from collections.abc import Callable, Sequence

def transform_all(values: Sequence[str], transform: Callable[[str], str]) -> list[str]:
    return [transform(value) for value in values]

print(transform_all((' Ada ', ' Lin '), str.strip))
Output
['Ada', 'Lin']
Back to quick reference ↑
04

Distinguish aliases, new domain types, and mapping records

A type alias gives a readable alternate spelling but does not create a distinct runtime type. NewType creates a checker-visible distinction with near-zero runtime wrapping cost. TypedDict describes dictionary keys and value types; it performs no runtime validation and is best for dict-shaped compatibility boundaries.

Typed dictionary remains a dictionary
from typing import NotRequired, TypedDict

class UserRow(TypedDict):
    id: int
    name: str
    nickname: NotRequired[str]

row: UserRow = {'id': 7, 'name': 'Ada'}
print(row['name'])
print(type(row).__name__)
Output
Ada
dict
Back to quick reference ↑
05

Relate types across a reusable operation

A generic parameter preserves relationships that object or Any would erase, such as returning the same element type accepted. Modern parameter syntax requires Python 3.12; TypeVar supports older versions. Bounds require subtype compatibility, while constraints choose from a closed set of alternatives.

Generic first using a TypeVar
from collections.abc import Sequence
from typing import TypeVar

T = TypeVar('T')
def first(values: Sequence[T]) -> T:
    if not values:
        raise ValueError('empty sequence')
    return values[0]

print(first([10, 20]))
print(first(('a', 'b')))
Output
10
a
Back to quick reference ↑
06

Express behavior structurally

A Protocol declares members an implementation must provide without requiring inheritance or registration. This reduces coupling for repositories, streams, clocks, and adapters. runtime_checkable tests only attribute presence and does not validate signatures or type arguments, so it is not a full runtime contract checker.

Structural close capability
from typing import Protocol

class SupportsClose(Protocol):
    def close(self) -> None: ...

class Resource:
    def close(self) -> None:
        print('closed')

def finish(resource: SupportsClose) -> None:
    resource.close()

finish(Resource())
Output
closed
Back to quick reference ↑

Local code tester

Explore structural typing

Define a protocol and use an unrelated implementation that satisfies the same behavior.

Runs in your browser
Output
Press Run to load Python locally.

Sources and further reading

References

Authoritative documentation used to verify and expand this cheat sheet.

  1. Python Software Foundationtyping — Support for type hintsdocs.python.org
  2. Python Typing CouncilTyping specificationtyping.python.org
  3. Python Software FoundationPython Tutorial: Function annotationsdocs.python.org

Help us improve

Found a typo or missing example?

Tell us what would make this cheat sheet clearer, more complete, or more useful.

Share feedback