The C# unions as described are discriminated unions.
The fact that they flatten a union of optional types into an optional union of the corresponding non-optional types is indeed a weird feature, which I do not like, because I think that a union must preserve the structural hierarchy of the united types, e.g. a union of unions must be different from a union of all types included in the component unions, and the same for a union of optional types, where an optional type is equivalent with a union between the void/null type and the non-optional type, but this C# behavior still does not make the C# unions anything else but discriminated unions, even if with a peculiar feature.
> that a union must preserve the structural hierarchy of the united types, e.g. a union of unions must be different from a union of all types included in the component unions, and the same for a union of optional types, where an optional type is equivalent with a union between the void/null type and the non-optional type
This is exactly the difference between simple union types and discriminated unions. This c# feature is what typescript has, not what Haskell/java/f#, etc.
The word "discriminated" by itself does not specify this property.
"Discriminated" just means that at run time you can discriminate the values of a union type by their current type, so you can use them correctly in expressions that expect one of the component types.
I agree that the right implementation of discriminated types is that mentioned by you and which is that of many important programming languages, but even if I disapprove of this property that the C# unions have, which in my opinion may lead to unexpected behavior that can cause subtle bugs, the C# unions are still discriminated unions, where you can discriminate the current type at run-time, with a "switch".
In my opinion, one should avoid this weird behavior of C#, by always defining only unions of non-optional types. Where needed, one should then define an optional type having as base a union type. Then these unions will behave like the discriminated unions of other languages.
Whether you use or not this policy, there are types that the C# unions cannot express, but if you use this policy, at least the limitations become explicit.