Skip to the content.
Type Safety: Mastering Union Types in Python | AI Systems Design From Scratch

Connect with Amin Boulouma Official

🏠 Documentation Hub 📝 Engineering Blog 💻 GitHub Repository

Precision with Union Types

Amin Boulouma, Software Engineer

As Python’s type hinting system has matured, Union types have become an essential tool for expressing that a function or variable can hold one of several different types. By using Union, you move away from the ambiguity of “it could be anything” and toward a self-documenting, statically verifiable codebase.

The Problem: The Ambiguity of Any

When a function accepts a parameter that could be either an int or a str, the lazy approach is to use Any or no type hint at all. This forces the reader (and static analysis tools like mypy) to guess what the code expects, leading to runtime errors that could have been caught during development.

The Solution: Using Union

A Union type explicitly defines the set of allowed types. If a value does not match one of these types, your IDE and type checker will flag it immediately.

Implementation

from typing import Union

def process_id(identifier: Union[int, str]) -> str:
    # Python 3.10+ also supports the pipe syntax: int | str
    return f"Processing ID: {str(identifier)}"

# These are valid
process_id(123)
process_id("abc-456")

# This would trigger a type-checker error
# process_id(None)

Modern Syntax: The Pipe Operator (|)

In Python 3.10 and newer, the Union import is largely optional. You can use the more concise pipe operator (|), which is visually clearer and follows standard set notation.

# The modern, cleaner way
def format_input(data: int | str | list[int]) -> str:
    ...

When to Use Union Types

  1. Flexible APIs: Use Union when a function is designed to handle multiple common input types, like an ID that can be a numeric index or a unique string slug.
  2. Optional Values: While Optional[T] is the standard for “T or None”, Union[T, None] is technically identical. Use | None for modern, readable code.
  3. Result Normalization: When a function can return different “shapes” of data (e.g., a Success object or an Error object), Union helps the caller handle those cases explicitly.

Best Practices

By being explicit about the types your code handles, you shift the burden of validation from your runtime logic to your development environment. This leads to fewer bugs and a much better developer experience.

Connect with Amin Boulouma Official