io-ts icon indicating copy to clipboard operation
io-ts copied to clipboard

Add `enum` support

Open alecgibson opened this issue 6 years ago • 11 comments

Fixes https://github.com/gcanti/io-ts/issues/67 Fixes https://github.com/gcanti/io-ts/issues/216

The enum is a popular aspect of TypeScript, and can be embedded in interfaces as a concise, but descriptive, shorthand for literal string unions:

enum Colour {
  White = '000000',
  Black = 'ffffff'
}

This change adds an exported member enum to io-ts, based on this suggestion by @noe132

It means that enums can be reused directly in io-ts:

const T = t.enum(Colour)

alecgibson avatar Sep 17 '19 16:09 alecgibson

One place this does seem to fall down is that you lose the casting when extracting an interface. I can't quite figure out how to do that (possibly something to do with the encode function?). For example:

enum Colour {
  White = '000000'
}

const Style = t.type({
  colour: t.enum(Colour)
})

interface IStyle extends t.TypeOf<typeof Style> {}

const style: IStyle = {
  colour: Colour.White
}

const colour = style.colour // colour has type "any" rather than "Colour"

I tried tweaking the signature for enumType:

const enumType = <E, A = E[keyof E]>(e: E, name: string = 'Enum'): EnumType<A> => {
  const is = (u: unknown): u is A => Object.keys(e).some((k) => e[k as keyof E] === u)
  return new EnumType<A>(name, is, (u, c) => (is(u) ? success(u) : failure(u, c)), identity)
}

...which seems to only succeed in disallowing a decode to string (which I guess is correct?), but still doesn't correctly cast.

alecgibson avatar Sep 18 '19 07:09 alecgibson

This looks like it might work better, for string enums at least...

const enumType = <E>(
  e: { [key: string]: E },
  name: string = 'Enum',
): EnumType<E> => {
  const is = (u: unknown): u is E => Object.keys(e).some(k => e[k] === u);
  return new EnumType<E>(
    name,
    is,
    (u, c) => (is(u) ? t.success(u) : t.failure(u, c)),
    t.identity,
  );
};

jessmorecroft avatar Feb 16 '20 10:02 jessmorecroft

Re: [gcanti/io-ts] Add enum support (#366)

2020-02-16 2:56 غرينتش-08:00, jessmorecroft [email protected]:

This looks like it might work better, for string enums at least...

const enumType = <E>(
  e: { [key: string]: E },
  name: string = 'Enum',
): EnumType<E> => {
  const is = (u: unknown): u is E => Object.keys(e).some(k => e[k] === u);
  return new EnumType<E>(
    name,
    is,
    (u, c) => (is(u) ? t.success(u) : t.failure(u, c)),
    t.identity,
  );
};

-- You are receiving this because you are subscribed to this thread. Reply to this email directly or view it on GitHub: https://github.com/gcanti/io-ts/pull/366#issuecomment-586692616

saa18877433 avatar Feb 17 '20 20:02 saa18877433

Hey, since number enums have remapping, https://www.typescriptlang.org/docs/handbook/enums.html#reverse-mappings I think it's essential to check if the key is the remapped one.

enum E {
  A = 1
}

// compiles to
const E {
  A: 1,
  '1': 'A',
}

In this case, 1 is valid, but 'A' is not. https://www.typescriptlang.org/play?#code/KYOwrgtgBAolDeAoKUCCUC8UCMiC+QA

I've updated my code here https://github.com/gcanti/io-ts/issues/216#issuecomment-471497998

noe132 avatar Apr 18 '20 16:04 noe132

Any update on this?

abrkn avatar Aug 06 '20 01:08 abrkn

+1 this would be a great addition, as currently I need to manually copy/paste my enum values into a t.union of t.literals

aroman avatar Jan 04 '21 00:01 aroman

Okay I've updated this, and it now correctly casts!

Screen Shot 2021-01-04 at 16 40 49

Also updated to include @noe132 's update for checking for reverse mapping

alecgibson avatar Jan 04 '21 16:01 alecgibson

Any update on this from @gcanti ?

Andrapyre avatar Jul 26 '21 19:07 Andrapyre

Hi, what is the current status of this feature? We'd love to have enum support :)

halbich avatar Dec 02 '21 07:12 halbich

@gcanti How do you feel about merging this in?

taylor-shift avatar Oct 26 '22 21:10 taylor-shift

Is there anything stopping this feature from being merged?

martindines avatar Apr 11 '23 13:04 martindines