This is entirely my opinion, but I think that while they are equivalent to the type checker, they express very different intentionality to the human reader. So they are not interchangeable.
If you're declaring a default value, then type it as def foo(bar: Optional[int] = None)
which declares an explicit intent that you don't have to pass in an argument.
However, if you don't have a default value, it may be declared def foo(bar: int | None)
, which declares that this is a required argument where None
is one of the valid argument value.
Most of the time, you want to do the former, so most of the time you should still use Optional[int]
; but there may be situations where requiring that callers explicitly pass None may be desirable.
A secondary reason is that writing def foo(bar: int | None = None)
just looks ugly and confusing with the repeated None
.
Optional
was invented to indicate "type or None". It's intrinsic meaning is "type or None", so following good basic principles, using thetype | None
is more explicit. It also gets rid of an identifier. These are objective reasons for going with the new syntax as long as you're sure you've got the Python version to support it. – Sharie