zod icon indicating copy to clipboard operation
zod copied to clipboard

Ability to "name" an enum

Open jasonkuhrt opened this issue 1 year ago • 2 comments

Consider this Zod-to-Nexus function:

export const enumType = <
  S extends
    | z.ZodNativeEnum<any>
    | z.ZodEnum<[string, ...string[]]>
    | z.ZodUnion<[z.ZodLiteral<string>, ...z.ZodLiteral<string>[]]>
>(
  schema: S,
  config?: { name: string; case?: 'camel' | 'snake' }
) => {
  const name = config?.name ?? schema._def.typeName
  const enumifyThis = enumify(config?.case ?? 'snake')
  const members =
    'options' in schema._def
      ? // union case
        schema._def.options.map((_) => _._def.value).map(enumifyThis)
      : // enum case
      Array.isArray(schema._def.values)
      ? schema._def.values.map(enumifyThis)
      : values(schema._def.values)
          .map((_) => String(_))
          .map(enumifyThis)
  return Nexus.enumType({
    name,
    members,
  })
}

When using it today it is not possible to capture the enum name so we get code like this:

const ExternalErrorDatabaseKind = z.enum([
   'IntrospectionFailure',
   'NotEmpty',
   'PushSchemaFailure',
   'SeedFailure',
])

NexusZod.enumType(ExternalErrorDatabaseKindEnum, {
   name: 'ExternalErrorDatabaseKind',
})

What I wish we could do is just:

NexusZod.enumType(ExternalErrorDatabaseKindEnum)

Which would I guess be powered by a new Zod method on enums like this:

const ExternalErrorDatabaseKind = z.enum([
   'IntrospectionFailure',
   'NotEmpty',
   'PushSchemaFailure',
   'SeedFailure',
]).name(`ExternalErrorDatabaseKind`)

Or maybe unify name and description under the concept of "metadata":

const ExternalErrorDatabaseKind = z.enum([
   'IntrospectionFailure',
   'NotEmpty',
   'PushSchemaFailure',
   'SeedFailure',
]).metadata({ name: `ExternalErrorDatabaseKind`, description: 'foobar' })

jasonkuhrt avatar Aug 31 '22 11:08 jasonkuhrt

I'm still hesistant to add additional metadata beyond the .describe method I implemented after our earlier discussion: https://github.com/colinhacks/zod/issues/677 Though it does seem like that would be adequate?

Even with a Zod method for naming a schema, there wouldn't be a statically detectable difference between z.enum([...]) and z.enum(...).name('MyEnum'). Which means there would be no way to prevent users at compile time from doing NexusZod.enumType(z.enum([...])). I guess this would just throw at runtime?

colinhacks avatar Sep 06 '22 01:09 colinhacks

I'm still hesistant to add additional metadata beyond the .describe method

Yeah I think that's quite reasonable! While describe is genuine information not found elsewhere name looks less like that. So yeah, not sure this belongs in here.

I guess this would just throw at runtime?

Yeah guess it would have to. I suspect baking the "named"ness of a zod type would add a big breaking change for something that looks to have dubious value.

Maybe other use-cases for "naming" types can be found. For example maybe schemas with names could be leveraged for improved error messages.

But on the whole it seems like an awkward feature request right now.

Maybe the ability to attach metadata would be another solution without baking in any assumptions.

const ExternalErrorDatabaseKind = z.enum([
   'IntrospectionFailure',
   'NotEmpty',
   'PushSchemaFailure',
   'SeedFailure',
]).metadata({ foo: '', bar: false, qux: 3 })

jasonkuhrt avatar Sep 06 '22 18:09 jasonkuhrt

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

stale[bot] avatar Nov 05 '22 19:11 stale[bot]