Skip to content

TypeError: unsupported operand for | in type hints ​

Problem Statement ​

When defining a Python dataclass with type annotations using the union operator (|), you might encounter this cryptic error:

text
TypeError: unsupported operand type(s) for |: 'type' and 'NoneType'

This frequently occurs in code similar to this example:

python
from dataclasses import dataclass

@dataclass
class InventoryItem:
    """Class for keeping track of an item in inventory."""
    name: str | None = None  # This causes the error
    unit_price: float
    quantity_on_hand: int = 0

The core issue arises when using the | operator for type unions (like str | None) in Python versions before 3.10. The pipe operator wasn't supported for type annotations until Python 3.10 was released, so this syntax fails in earlier versions like Python 3.9.

Solution ​

Using Optional for None-Capable Types ​

Instead of str | None, use Optional[str] from the typing module:

python
from dataclasses import dataclass
from typing import Optional  # Import Optional

@dataclass
class InventoryItem:
    name: Optional[str] = None  # Correct for Python <3.10
    unit_price: float
    quantity_on_hand: int = 0

Using Union for Complex Type Combinations ​

For multi-type unions (not just None), use Union from typing:

python
from typing import Union

# Equivalent to str | int | float - Works in older Python
rating: Union[str, int, float] = "Excellent"

# Equivalent to str | int | None - Works with Optional too
identifier: Union[str, int, None] = 1001

Version Compatibility Note

The | union syntax (str | None) was introduced in PEP 604 and only works in Python 3.10 and later. For earlier versions, you must use Optional and Union.

Why Other Approaches Fail ​

Avoid trying these non-solutions:

python
# WON'T WORK: 'or' isn't valid for type hints
name: str or None = None

# WON'T WORK: Import location doesn't change behavior
from typing import Optional | Union

Key Insights ​

  1. The | operator in type hints is a Python 3.10+ feature
  2. For Python 3.9 and earlier:
    • Use Optional[T] instead of T | None
    • Use Union[T1, T2, ...] instead of T1 | T2 | ...
  3. Optional[T] is equivalent to Union[T, None] but clearer for "nullable" values
  4. Type hints don't affect runtime behavior - they're only for static type checkers

Comparison Table: Syntax by Python Version ​

SyntaxPython ❤️.10Python 3.10+
str | None❌ Error✅ Valid
Optional[str]✅ Valid✅ Valid
int | str | float❌ Error✅ Valid
Union[int, str, float]✅ Valid✅ Valid

Recommendation ​

If maintaining compatibility with Python ❤️.10:

  1. Replace all T | None type hints with Optional[T]
  2. Replace complex unions (A | B | C) with Union[A, B, C]
  3. Check your Python version:
    python
    import sys
    print(sys.version)  # Verify your runtime version

For projects using Python 3.10+, you can safely use the cleaner | syntax:

python
# Clean modern syntax (Python 3.10+)
name: str | None = None
rating: str | int | float = 5.0

Migration Path

If upgrading to Python 3.10+ is feasible, modern type hint syntax offers:

  • More readable unions
  • Clearer syntax for optional values
  • Better IDE support in recent versions

Conclusion ​

The "unsupported operand type(s) for |" error occurs exclusively when using modern type union syntax (X | Y) in Python versions before 3.10. The solution is to replace:

  • T | None with Optional[T]
  • T1 | T2 | ... with Union[T1, T2, ...]

This maintains compatibility while clearly expressing type intentions. Always verify your project's Python runtime version and use type hints accordingly. For new projects, consider targeting Python 3.10+ to leverage the cleaner union syntax.