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:
TypeError: unsupported operand type(s) for |: 'type' and 'NoneType'
This frequently occurs in code similar to this example:
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:
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
:
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:
# 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
- The
|
operator in type hints is a Python 3.10+ feature - For Python 3.9 and earlier:
- Use
Optional[T]
instead ofT | None
- Use
Union[T1, T2, ...]
instead ofT1 | T2 | ...
- Use
Optional[T]
is equivalent toUnion[T, None]
but clearer for "nullable" values- Type hints don't affect runtime behavior - they're only for static type checkers
Comparison Table: Syntax by Python Version
Syntax | Python ❤️.10 | Python 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:
- Replace all
T | None
type hints withOptional[T]
- Replace complex unions (
A | B | C
) withUnion[A, B, C]
- 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:
# 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
withOptional[T]
T1 | T2 | ...
withUnion[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.