codedownio / aeson-typescript

Generate TypeScript definition files from your ADTs
BSD 3-Clause "New" or "Revised" License
59 stars 27 forks source link

Always generate "tagged" unions #26

Closed beezee closed 3 years ago

beezee commented 3 years ago

I'm curious if you'd be open to considering this change, I'd be curious to see how much a lift it'd be to work on a PR.

The blocker I have in using this to generate typescript is that in Haskell, all unions are disjoint, e.g.

data D a = A Int | B Int | C a has no overlap between constructors A and B, even though the remainder of the types are the same.

If the generated typescript for this is

type D<T> = A<T> | B<T> | C<T>;

type A<T> = number;

type B<T> = number;

type C<T> = T

then there's no distinction between constructors A and B in the typescript code. The generally accepted pattern for this in typescript is to "tag" unions, essentially pairing each type of the union with a literal for it's constructor. So you'd have instead something like:

type D<T> = A<T> | B<T> | C<T>;

type A<T> = {tag: "A", value: number};

type B<T> = {tag: "B", value: number};

type C<T> = {tag: "C", value: T}

which produces a type that is actually equivalent to the Haskell type that generated it.

Thoughts?

thomasjm commented 3 years ago

Hello -- not sure I understand. This library doesn't get to decide what the JSON representation is for a given Haskell type. It has to match the output produced/parsed by the corresponding Aeson instances, for a given set of Aeson Options.

Of course, you can get the tagged style output by using the TaggedObject constructor for the SumEncoding option.

thomasjm commented 3 years ago

Going to close this for now, let me know if there's more discussion needed and we can re-open.