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.