Get AI summaries of any video or article — Sign up free
Python Tutorial: Type Hints - From Basic Annotations to Advanced Generics thumbnail

Python Tutorial: Type Hints - From Basic Annotations to Advanced Generics

Corey Schafer·
5 min read

Based on Corey Schafer's video on YouTube. If you like this content, support the original creators by watching, liking and subscribing to their content.

TL;DR

Use a type checker like MyPy to turn type hints into actionable feedback; annotations alone don’t enforce correctness.

Briefing

Type hints in Python become genuinely useful only when paired with a type checker—otherwise they’re mostly documentation. Using MyPy (via the VS Code extension or the command line), the tutorial demonstrates how adding annotations can surface incompatible assignments, incorrect function arguments, and mismatched return types before bugs ship.

The walkthrough starts with straightforward annotations: variables annotated as `str` or `int` trigger MyPy errors when reassigned to the wrong type. It then moves into function signatures, where type hints start doing real work. A `create_user` function is annotated so `first_name` and `last_name` must be `str`, `age` must be `int` but also optional (expressed as `int | None`), and the return type is refined from a generic `dict` to a dictionary with specific key/value types. The tutorial highlights a common gotcha: an optional parameter with a default of `None` must be represented as a union type, or the type checker will complain.

From there, the return type evolves to show different levels of precision. A type alias can name a complex dictionary shape to keep function definitions readable, but type aliases still allow “structural” mistakes when two different concepts share the same underlying structure. The example introduces `favorite_color` as an RGB tuple type. When a developer accidentally passes an HSL tuple (also three integers), MyPy won’t catch it if both are just plain `tuple[int, int, int]`. Switching from a type alias to `NewType` fixes that by making RGB and HSL distinct types even though they share the same runtime representation.

Next, the tutorial shows why “more specific outputs” matter. A type alias for the user dictionary allows any value to be one of several types, but it won’t necessarily validate each key independently. To enforce per-field correctness, it replaces the alias with a `TypedDict`, specifying the exact type for each key (e.g., `age: int | None`, `favorite_color: RGB | None`). A contrived conversion of `age` into a string demonstrates MyPy catching the error once the typed dictionary is in place.

Finally, the tutorial upgrades the data model again by converting the `TypedDict` into a `@dataclass`, turning the “user” shape into a real class with typed attributes. It then moves into generics with a `random_choice` helper: naïvely using `any` preserves flexibility but destroys IDE usefulness because autocomplete can’t infer the returned type. Using a `TypeVar` (and the newer Python 312 shorthand syntax) keeps input and output types linked—passing a list of users returns a user, while passing a list of strings returns a string.

The practical section closes with third-party libraries: MyPy may treat untyped dependencies as `Any` unless stub packages are installed. Installing `types-requests` enables proper checking of values like `response.status_code` (an integer), so assigning it to a string triggers warnings. The tutorial ends with best practices: start small, keep inputs generic and outputs specific, and gradually annotate critical parts of a codebase to improve readability, IDE completion, and early bug detection. It also teases future coverage of Pydantic for runtime validation and an AsyncIO-focused series.

Cornell Notes

Type hints in Python provide real value when a type checker like MyPy validates them. The tutorial shows how annotations catch issues such as incompatible variable assignments, wrong argument types, and incorrect return types—especially when optional values are represented as unions like `int | None`. It demonstrates increasing precision for structured data: plain dictionaries → typed dictionaries → `TypedDict` with per-key types → `@dataclass` models. It then introduces generics to keep helper functions type-safe and IDE-friendly, contrasting `any` (loses autocomplete) with `TypeVar` (preserves the relationship between input and output types). Finally, it covers third-party stubs (e.g., `types-requests`) so MyPy can type-check libraries that otherwise default to `Any`.

Why doesn’t adding type hints automatically give type checking in Python?

Type hints are metadata; Python itself doesn’t enforce them. A separate type checker (like MyPy) must analyze the annotated code to produce errors or warnings. The tutorial uses MyPy in VS Code (Problems tab) or via command line to show “success, no issues found” before annotations are added and warnings once types are violated.

How should an optional parameter be annotated when it defaults to `None`?

The parameter must be a union type that includes `None`. For example, if `age` is optional and defaults to `None`, annotate it as `int | None`. The tutorial also notes that older code may use `Optional[int]` imported from `typing`, but modern syntax prefers the `|` union form.

What’s the difference between a type alias and `NewType`, and why does it matter for RGB vs HSL?

A type alias names an existing type shape, but it doesn’t create a distinct type. If both RGB and HSL are represented as `tuple[int, int, int]`, MyPy can’t detect mixing them when they’re just aliases of the same underlying structure. `NewType` creates distinct nominal types, so passing an HSL tuple where RGB is expected triggers a MyPy error, even though both are three integers at runtime.

Why does `TypedDict` catch more mistakes than a dictionary type alias?

A type alias for a dictionary can allow broad value unions without validating each key’s specific type. `TypedDict` specifies the type of every key individually (e.g., `age: int | None`, `favorite_color: RGB | None`). That per-field precision lets MyPy flag errors when a specific field is converted to the wrong type (like casting `age` to `str`).

How do generics improve both type safety and IDE autocomplete compared with `any`?

Using `any` makes the function accept and return unknown types, so the IDE can’t infer what comes back—autocomplete degrades. Using `TypeVar` ties the return type to the input type: `random_choice` accepts `list[T]` and returns `T`. Then passing a `list[User]` yields a `User` return type for autocomplete, while passing a `list[str]` yields a `str`.

What are stub packages, and why are they needed for third-party libraries?

If a third-party package ships without type information, MyPy treats its values as `Any`, reducing error detection. Stub packages (commonly named `types-<package>`) provide type definitions. The tutorial installs `types-requests` so MyPy can correctly infer that `response.status_code` is an integer and warn when it’s reassigned to a string.

Review Questions

  1. When annotating an optional parameter with a default of `None`, what union form should be used, and why?
  2. In the RGB vs HSL example, what specific change makes MyPy catch the accidental swap: type aliasing or `NewType`? Explain the underlying reason.
  3. How does `TypeVar` preserve the relationship between a generic function’s input and output types, and what effect does that have on IDE autocomplete?

Key Points

  1. 1

    Use a type checker like MyPy to turn type hints into actionable feedback; annotations alone don’t enforce correctness.

  2. 2

    Represent optional values with unions such as `int | None` when defaults are `None`.

  3. 3

    Refine return types progressively—from generic `dict` to key/value-specific dictionaries—to increase the number of bugs caught early.

  4. 4

    Use type aliases for readability, but use `NewType` when two concepts share the same underlying structure and must be treated as distinct types.

  5. 5

    Prefer `TypedDict` when you need per-key type validation for dictionary-like data structures.

  6. 6

    Use `@dataclass` when the data model should behave like a real class while still benefiting from typed attributes.

  7. 7

    For reusable helpers, use generics with `TypeVar` (not `any`) to keep both type checking and IDE autocomplete accurate; install stub packages (e.g., `types-requests`) for third-party libraries lacking type info.

Highlights

MyPy only becomes useful after type hints are paired with a type checker; the tutorial shows warnings appearing once annotations are added.
`int | None` is the correct modern way to express optional parameters with `None` defaults, avoiding type-checker complaints.
`NewType` prevents RGB/HSL mixups that slip through when both are just `tuple[int, int, int]`.
`TypedDict` enforces types per key, catching errors that a broad dictionary type alias can miss.
`TypeVar` keeps generic helpers IDE-friendly by linking the return type to the input type, unlike `any`.

Topics

  • Type Hints Basics
  • Optional Unions
  • Type Aliases
  • NewType
  • TypedDict
  • Generics
  • TypeVar
  • Third-Party Stubs
  • MyPy

Mentioned