There are number of ways of creating tagged unions.
type X = { type: A, prop: number } | { type: B, prop: string };
type Y = { type: A, data: { prop: number } } | { type: B, data: { prop: string } };
type Z = ['A', { prop: number }] | ['B', { prop: string }];
Consider that generally in tagged unions, the tag is not part of the type being unioned.
This is because the tag is only necessary for disambiguation.
If you have disambiguated, then the tag
is unnecessary.
In the examples above, only Y
and Z
well formed, because they can also be recursive.
In X
, the tagged union can conflict with the type
property of the unioned types.
I've started to prefer Z
because it is has less syntax overhead, and it more closely matches how Haskell does it.
In fact in haskell, the Z
lends itself easily to pattern matching, where one can pattern match the first element, then use the underlying data.
In TS, we have to do something like this:
const [type, data] = z;
switch (type) {
case 'A':
const { prop } = data;
break;
case 'B':
const { prop } = data;
break;
}