Reputation: 1089
So I was reading the official docs of Typescript about union types, and I was thinking that it is the same as "discriminated unions" in F# (granted they have different syntax but same concept), as I have a F# background and given the fact that both are backed by Microsoft. But looking at the docs, F# doesn't really make a distinction between "union types" and "discriminated unions": https://fsharpforfunandprofit.com/posts/discriminated-unions/
However, Typescript does make a distinction between these two concepts:
Union types: https://www.typescriptlang.org/docs/handbook/advanced-types.html#union-types
Discriminated Unions: https://www.typescriptlang.org/docs/handbook/advanced-types.html#discriminated-unions
So I was wondering if there is really a distinction in the concepts themselves or is just some language dependent concept?
What I understand so far is that union types in F# are also discriminated unions because you can discriminate the union type using match expressions and deconstructing.
However, you cannot do the discrimination with Typescript as the language does not provide a specific expression to do that, so yo need to discriminate by a value, that all union types have, the discriminant. Is this correct?
Upvotes: 6
Views: 3600
Reputation: 3069
The main difference is that, Typescript Union Type is actually a superset of F# Discriminated Union.
TypeScript Union Type = Untagged union type.
F# Discriminated Union = Tagged union type.
In other words, every discriminated union that can be modelled in F# can be modelled isomorphically in Typescript union type, however the reverse is not true.
For example, the following discriminated union in F#:
type a' Option = Some of 'a | None
Can be modelled isomorphically in Typescript as:
type Option<T> = {tag: 'Some', value: T} | {tag: 'None'}
However, the following Typescript union type cannot be modelled isomorphically in F#:
type UserInput = number | string
The main difference here is TypeScript union type do not need be tagged, however F# union type must be tagged.
Thus, we can see that TypeScript is actually more flexible than F#, however this does not come without cost, untagged union are actually holey, meaning there are some type union where TypeScript will fail to type-check.
It's like untype lambda calculus is superset of typed lambda calculus, but type lambda calculus is much more easier to proof right.
Upvotes: 9
Reputation: 144176
The operands in a type union (A | B)
are both types, while the cases in a discriminated union type U = A | B
are both constructors for the type U
and are not types themselves. The values of type U
are tagged at runtime so you can distinguish between the possible cases.
One consequence is that discriminated unions can be nested in a way union types might not. Optional values of some type A
within a union type system might be represented as
type A? = (A | null)
where null
is the singleton type for the null
value.
With a discriminated union it is usually represented as
type a' option = Some of 'a | None
With this formulation the value
let o: int option option = Some None
cannot be represented with the union type since (A?)?
== (A | null) | null
== A | null
== A?
Upvotes: 7