#Reducing boilerplate when replacing enums with Zod

13 messages ยท Page 1 of 1 (latest)

wraith geode
#

Morning everybody ๐Ÿ‘‹

I'm looking for a substitution for enums and so far I'm stuck with this pattern:

export const MyEnumSchema = z.enum({ A: 0, B: 2 /*...*/ });
export const MyEnum = MyEnumSchema.enum;
export type MyEnum = z.infer<typeof MyEnumSchema>;

To reduce boilerplate, I wrote a helper that returns all three values and types:

type EnumLike = Readonly<Record<string, string | number>>;
function e<const T extends EnumLike>(map: T) {
  const schema = z.enum<T>(map);
  return {
    schema,
    enum: schema.enum,
    Type: null as unknown as z.infer<typeof schema>,
  };
}

But I can't find a way to destructure the type cleanly on the other side:

export const {
  schema: ExampleEnumSchema,
  enum: ExampleEnum,
  // Type: ExampleEnum,
} = e({ A: 1, B: 2 });

Any thoughts on a pattern that avoids the boilerplate while still getting schema + runtime enum + inferred type?

timid hornetBOT
#
amatiasq#0

Preview:```ts
import z from 'zod';

function e<const T extends Readonly<Record<string, string | number>>>(map: T) {
const schema = z.enum<T>(map);
return {
schema,
enum: schema.enum,
Type: null as unknown as z.infer<typeof schema>,
};
}

export const {
...```

quaint berry
#

ah i get it now. you definitely cannot return a type from a function. types are not values

#

you could do this, but i would not recommend it:

timid hornetBOT
#
mkantor#0

Preview:```ts
import z from 'zod';

function e<const T extends Readonly<Record<string, string | number>>>(map: T) {
const schema = z.enum<T>(map);
return {
schema,
enum: schema.enum,
Type: null as unknown as z.infer<typeof schema>,
};
}

export const {
...```

quaint berry
#

someone is going to try and use that Type value at some point and hit runtime errors

wraith geode
#

yeah, I see, and it requires typeof in the usage point

tardy dome
vale garden
#

I suggest to do export type to make sure you're not giving the value

timid hornetBOT
#
quadristan#0

Preview:```ts
import z from 'zod';

function e<const T extends Readonly<Record<string, string | number>>>(map: T) {
const schema = z.enum<T>(map);
return {
schema,
enum: schema.enum,
Type: null as unknown as z.infer<typeof schema>,
};
}

const {
schema: ExampleEnumSchema,
...```

quaint berry
#

at that point i don't think the function is shaving off any real boilerplate; i'd just write this:

timid hornetBOT
#
mkantor#0

Preview:```ts
...
export const ExampleEnum = {A: 1, B: 2} as const
export type ExampleEnum =
typeof ExampleEnum[keyof typeof ExampleEnum]

export const ExampleEnumSchema = z.enum(ExampleEnum)
...```

quaint berry
#

(or what they had originally, i guess)