# Python Type Hints: A Primer for Reading AI Agent Code (2026)

> A Python type hints primer for reading AI agent code: function hints, Optional, Union, TypedDict for message lists, and how frameworks use them.

*Source: https://www.infowok.com/python-type-hints-primer/ · Sukhveer Kaur · Published June 22, 2026*

---

The first time agent code confuses a Python developer, it's rarely the logic — it's the labels. `def get_weather(city: str) -> dict:` has two of them before the body even starts, and a tool's arguments often arrive as a `TypedDict` or a Pydantic model you've never declared. This **Python type hints** primer names those labels once, so the rest of the code reads cleanly.

Here's the reassuring part: type hints don't change what your code *does*. They're notes for humans and tools. But agent frameworks read them to figure out how the model should call your functions, so recognising the syntax is the difference between following a tutorial and squinting at it. Let's clear it up.

<Prerequisites>

- You can read a basic Python function and a dictionary — new to that? The [Python for AI agents primer](/ai-agents-from-scratch-python-part-0/) covers both
- That's it — this primer assumes no prior typing knowledge

</Prerequisites>

<KeyTakeaways>

- **A type hint is a label, not a rule:** `city: str` documents intent, but Python doesn't enforce it at runtime.
- **Function hints** annotate each argument and the return value (`-> dict`) — the most common form you'll read.
- **`TypedDict` types a dictionary's shape** — exactly the `{"role": ..., "content": ...}` structure agent messages use.
- **Frameworks turn your hints into a schema** the model reads, which is why tool functions are always carefully typed.

</KeyTakeaways>

## What a type hint is — and what it isn't

A **type hint** is an annotation that says what kind of value something holds. You add it with a colon for variables and arguments, and an arrow for return values.

```python
name: str = "Asha"

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

The crucial thing to internalise early: **Python does not enforce hints at runtime.** The function above will happily run `add("a", "b")` and return `"ab"` — the hint is documentation, not a guard. Hints exist for *people* reading the code and for *tools* that check it. That's not a weakness; it's the design (see [PEP 484](https://peps.python.org/pep-0484/)). Enforcement is opt-in, and we'll get to who opts in.

## Function hints: arguments and return values

This is the form you'll meet most, because every agent tool is a typed function. Read it left to right: each parameter gets `name: type`, and `-> type` after the parentheses is what the function gives back.

```python
def get_weather(city: str) -> dict:
    """Return the current weather for a city."""
    return {"city": city, "temp_c": 31}
```

`city: str` says the input is a string; `-> dict` says you get a dictionary back. **When an agent framework sees this, it builds a description of the tool from exactly these hints** — the model learns the function takes a string called `city`. That's why untyped tools misbehave: with no hints, the model is guessing the arguments.

## The handful of types you'll actually see

Beyond `str`, `int`, `float`, and `bool`, a few container and "maybe" types cover most agent code.

- **`list[str]`, `dict[str, int]`** — a list of strings, a dict mapping strings to ints. The shape inside the brackets tells you what's stored.
- **`str | None`** — "a string *or* nothing." The `| None` part marks a value that might be absent (older code writes this as `Optional[str]`).
- **`int | str`** — a *union*: either an int or a string. You read `|` as "or".

```python
def find_user(user_id: int) -> dict | None:
    """Return the user record, or None if not found."""
    ...
```

That return hint, `dict | None`, is a tiny but useful signal: it warns you the function can hand back nothing, so your code should check before using the result.

<Callout type="note" title="Two ways to write the same thing">
`str | None` (Python 3.10+) and `Optional[str]` (any version, needs `from typing import Optional`) mean the identical thing. You'll see both in the wild — the `|` form is the modern style.
</Callout>

## TypedDict: typing the shape of a dictionary

Here's the one that unlocks agent code. A conversation is a list of dictionaries, each with a `role` and `content`. A **`TypedDict`** declares that exact shape — which keys exist and what type each value is.

```python
from typing import TypedDict

class Message(TypedDict):
    role: str
    content: str

messages: list[Message] = [
    {"role": "system", "content": "You are a helpful agent."},
    {"role": "user", "content": "What's the weather in Delhi?"},
]
```

Now `messages` isn't just "a list of dicts" — it's a list of `Message`, and a tool can see at a glance that every entry needs a `role` and a `content` ([PEP 589](https://peps.python.org/pep-0589/)). LangGraph uses this exact pattern for an agent's `State`, so once you recognise `TypedDict`, that part of framework code stops being mysterious.

## Where hints get teeth: Pydantic

If hints aren't enforced, how do agents avoid "the model returned garbage"? They bring in a library that *chooses* to enforce them. **[Pydantic](https://docs.pydantic.dev/)** takes a class of hints and validates real data against it, raising a clear error when the shape is wrong.

```python
from pydantic import BaseModel

class WeatherReport(BaseModel):
    city: str
    temp_c: int

report = WeatherReport(city="Pune", temp_c="hot")   # raises a validation error
```

This is the backbone of type-safe agents: you declare the shape you expect, and Pydantic guarantees you either get it or a loud failure — never silent nonsense three functions later. The [Pydantic AI tutorial](/pydantic-ai-tutorial-type-safe-agents-python/) shows this inside a full agent.

<Callout type="tip" title="Want runtime checks without Pydantic?">
A type checker like [mypy](https://mypy.readthedocs.io/en/stable/) reads your hints and flags mismatches *before* you run the code — `mypy agent.py` catches a `str` passed where an `int` was promised. It's the cheapest way to make hints earn their keep.
</Callout>

## Why Python type hints matter so much to agents

Pulling it together: in ordinary scripts, hints are a nicety. In agent code, Python type hints are load-bearing. **A tool's hints become the schema the model reads to call it; a `TypedDict` defines the state that flows through the graph; a Pydantic model guarantees the output is shaped the way the rest of your app expects.** Strip the hints and an agent framework is flying blind. That's why every well-written tool you'll copy is carefully typed — and why reading hints fluently makes all of it legible.

## Quick recap

The whole primer, in five lines:

- **A type hint is a label** (`city: str`, `-> dict`), not a runtime rule.
- **Function hints** annotate each argument and the return — the form you read most.
- **`str | None` and unions** flag "maybe absent" and "either/or" values.
- **`TypedDict`** types a dict's keys and values — the agent `messages`/`State` shape.
- **Pydantic and mypy** are how hints get enforced, on purpose.

## Frequently Asked Questions

**What are type hints?** Optional labels for the type of a value (`city: str`, `-> dict`). They document intent and feed tools; Python ignores them at runtime.

**Do hints change how code runs?** No. By default they're not enforced — a function hinted `-> int` can still return a string. Enforcement comes from mypy or Pydantic.

**What is a TypedDict?** A declaration of the exact keys and value types a dictionary should have — the structure agent message lists use.

**Do I need hints before building agents?** Read them first; tool schemas, LangGraph state, and Pydantic all rely on them. Writing thorough hints is a habit you grow into.

## Conclusion

Python type hints look like extra ceremony until you see what they're for: they're how agent frameworks learn the shape of your functions and data. Read `city: str` as "this argument is a string," `-> dict | None` as "returns a dict or nothing," and `TypedDict` as "a dictionary with these exact keys," and the typed parts of any agent tutorial turn from noise into signal.

**Which typed pattern trips you up most — `Optional`, unions, or `TypedDict`?** Tell me in the comments and I'll expand on it.

<NextSteps>

- **Need the Python basics first?** The [Python for AI agents primer](/ai-agents-from-scratch-python-part-0/) covers dicts, functions, and loops.
- **See hints enforced:** the [Pydantic AI tutorial](/pydantic-ai-tutorial-type-safe-agents-python/) builds a type-safe agent.
- **Ready to build?** [Tool calling in Python](/ai-agents-from-scratch-python-part-2/) turns typed functions into tools the model can call.

</NextSteps>
