Support for Enums
LcTwisk opened this issue · 7 comments
Let's say there is an enum like this:
enum Alphabet {
a = "a",
b = "b"
}
A possible decoder would be:
const decoder = either2(constant("a"), constant("b"))
This doesn't work for enums with more than 9 cases atm. What do you think of introducing another method that checks input agains an array of values with the same type?
Something like oneOf(values: [T])
I've only included "eithers" until either9, because you need to practically stop at a specific point. You can however just nest eithers if you need more than 9 cases. For example, if you need 13:
const either13 = either5(
either9(
... /* 9 cases here */
),
... /* 4 more cases */
)
Internally, either5
is also defined as either(either4(...), ...)
, so this isn't weird.
In your example of oneOf(values: [T])
, what would T
be? If you have oneOf(['foo', 'bar'])
and want a decoder for 'foo' | 'bar'
, then I don't think this works, as the input type of ['foo', 'bar']
would be inferred as string[]
, not ('foo' | 'bar')[]
. (So you would at best get a decoder for string
s, not a union of string literals.) Can you elaborate a bit, or perhaps provide an example of how it would work?
PS: I think you probably want to use constant()
rather than hardcoded()
in your example?
Ahh I see!
Didn't think about nesting the either methods, this will work for my use case I guess 👍
For TS enums it would still be useful to add something like:
export function enum_(members: Array<string | number>): Decoder<string | number> {
return (blob: mixed) => (members.includes(blob) ? Ok(blob) : Err(annotate(blob, `Must be one of ${members.join(', ')}`)));
}
PS: Yep, I meant constant
indeed, will change it :)
For TS enums it would still be useful to add something like
I agree that this would be a nice generic decoder to add to the library, as a convenience for enforcing a string (or number) value is one of a few preset alternatives. It's good to note however that the output of such a decoder will always be a string
at best, not a union of specific string constants.
If you have something like:
enum MyEnum {
foo = "foo",
bar = "bar",
}
const value = guard(oneOf(['foo', 'bar']))(request.body);
Then to the type system value
will just be a string
(even though at runtime this can only ever be 'foo'
or 'bar'
), but the type system doesn't know.
If you have a function that takes an Enum:
function doSomething(thing: MyEnum) { ... }
doSomething(value)
// ^^^^^ TS2322: Type 'string' is not assignable to type 'MyEnum'
To solve this, you will still have to use either(constant('foo'), constant('bar'))
.
That said, if you want to use oneOf()
as an alternative for something like regex()
(check specific string values at runtime but return a string type), then yes I agree it's still a nice/useful addition.
@LcTwisk Ideas?
Hi there, any update on this?
Update: Since 2.3.0, support for enums comes out of the box, see the docs.
Old answer
There is no specific decoder for native enums at the moment. In decoders 2.0, the most pragmatic way to build one is to use the following construction:
enum MyEnum {
foo = "foo",
bar = "bar",
}
const myEnumDecoder = oneOf(['foo', 'bar']) as Decoder<MyEnum>;