Optional means X or None; Union allows multiple types; X | Y is the concise PEP 604 syntax
Optional[X] is exactly equivalent to Union[X, None]. It signals that a value can be X or None. Union[X, Y] means the value can be either X or Y, and can be extended to more types. Before Python 3.10, you had to import these from typing. PEP 604 introduced the | operator, so you can write X | Y directly, and it works at runtime with isinstance and issubclass. The | syntax is cleaner and avoids the import, but it requires Python 3.10+. The typing.Optional and typing.Union forms are still valid and necessary for older versions. One subtlety: Optional[X] does not mean the parameter is optional; it means the value can be None. A parameter with a default value is optional in the calling sense, which is a different concept.
Optional[X] == Union[X, None]. Use it when None is a valid value.
Union[X, Y] allows any of the listed types. Order does not matter for type checking.
X | Y (PEP 604) is the modern syntax, available in 3.10+. It works with isinstance and issubclass.
You can mix: int | None, str | bytes, and even nested unions.
Trade-off: the | syntax is concise but not available in older codebases. typing.Optional is more explicit for readers unfamiliar with PEP 604.
Common mistake: thinking Optional[X] means the parameter has a default. It does not.
Common mistake: using X | Y in Python 3.9 or earlier, which raises TypeError at runtime when evaluating the annotation.
Version note: PEP 604 landed in 3.10. For 3.7-3.9, use from future import annotations to postpone evaluation, or stick to typing.Union and typing.Optional.
0-2 years experience
2-5 years experience
5-8 years experience
8+ years experience