Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enum decoders are cumbersome and not intuitive #18

Open
mulias opened this issue May 22, 2018 · 6 comments
Open

Enum decoders are cumbersome and not intuitive #18

mulias opened this issue May 22, 2018 · 6 comments

Comments

@mulias
Copy link
Contributor

mulias commented May 22, 2018

Given a TS enum

enum Directions { North, South, East, West }

We currently write the decoder as

const directionsDecoder: Decoder<Directions> = oneOf(
  constant(Directions.North), 
  constant(Directions.South), 
  constant(Directions.East), 
  constant(Directions.West)
);

which isn't great.

It's worth noting that the most ideal syntax is not possible, since TS compiles out enums and hardcodes the enum values during compilation

const directionsDecoder = enumDecoder(Directions); // this doesn't work!

Something like this should be possible:

const enumDecoder = <E>(...enumValues: Array<E[keyof E]>): Decoder<E> =>
  oneOf(...enumValues.map((v) => constant(v)));

const directionsDecoder = enumDecoder(
  Directions.North, Directions.South, Directions.East, Directions.West
);

But no matter how I bash at the types I am unable to get the type checker to reconcile the relationship between the elements of the enum and the enum in total.

@RocketPuppy
Copy link
Contributor

@mulias This says that "enums are real objects at runtime" https://www.typescriptlang.org/docs/handbook/enums.html, so I would expect enumDecoder(Directions) to work. Is that a recent change?

@mulias
Copy link
Contributor Author

mulias commented May 22, 2018

@RocketPuppy I had looked into this a while ago and had forgotten some of the details. The issue I found is that if you define an enum then it has an object at runtime which can be used to create a decoder, but if you define a const enum it is removed during compilation. My concern was that it seemed like there was no static warning about using a const enum as if it existed at runtime, and I didn't want to have special documentation that said "this helper will only work for non-const enums and will otherwise fail at runtime without warning." Maybe I need to re-investigate that option.

I should note that shortly after posting this issue I realized that it's totally possible to write the enum decoder inside of the Decoder class defined in this library, but it can't be done as a helper function like I was attempting above.

@RocketPuppy
Copy link
Contributor

Ah, I thought it might have been an issue with const enums. You could have two decoders. One that takes an enum object and one that takes enum members. I think Typescript should prevent you from using the const enum with the enum object decoder.

@mulias
Copy link
Contributor Author

mulias commented May 23, 2018

Only one way to find out :)

@mulias mulias closed this as completed May 23, 2018
@mulias mulias reopened this May 23, 2018
@mulias
Copy link
Contributor Author

mulias commented Jun 1, 2018

New issue with enumDecoder(Directions)

Object.keys(Directions)
// [  '0', '1', '2', '3', 'North', 'South', 'East', 'West' ]

Enum runtime objects are implemented as two way mappings, so if all I have is the enum then I can't actually tell which values are the keys.

All the ways around that seem pretty hacky, so I think we're stuck with listing out the enum values.

@katanacrimson
Copy link

It appears that the behavior is heavily intended, and is also specific to numeric values for enums.

If the enum uses string values, no reverse mapping happens - so at least for string enums, it becomes possible to make the assumption that you can coerce the enum into an array and use an Array.includes() call to verify if the enum is a member. However, there's no easy way to verify if the enum is a string enum or not, making it something that can only be done by hand.

More info here: https://www.typescriptlang.org/docs/handbook/enums.html#reverse-mappings
Also related: microsoft/TypeScript#30487

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants