That's roughly it. I was referring to a difference at the type system level, rather than the runtime level, though.
Disjoint unions aka sum types (as seen in Haskell/ML etc) entail some runtime way of keeping track which side of the sum any given inhabitant lies on. Tags are one way to do this, but e.g. in a language where pointers/references are never the zero machine word, and unit is represented as the zero word, no tag would be needed for the type Maybe String.
Union types (as seen in Pony, apparently, and also in Typed Racket), by contrast, /don't/ preserve information about which side of the union an inhabitant came from.
So, disjoint types: A + A ≠ A
but union types: A ∪ A = A
So expanding (Maybe X) into (X + 1), we have that (Maybe (Maybe X)) = ((X + 1) + 1) ≠ X + 1,
but if instead (Maybe X) expands to (X ∪ 1), we have that (Maybe (Maybe X)) = ((X ∪ 1) ∪ 1) = X ∪ 1 = (Maybe X).
This is one of the problems with uses of null in languages that have it.