zod icon indicating copy to clipboard operation
zod copied to clipboard

enum of numbers

Open amatiasq opened this issue 3 years ago • 2 comments

This code fails:

import { z } from "zod";

const Multipliers = [1, 2, 3, 4] as const;

const schema = z.enum(Multipliers);

With error

No overload matches this call.
  Overload 1 of 2, '(values: readonly [string, ...string[]]): ZodEnum<[string, ...string[]]>', gave the following error.
    Argument of type '[1, 2, 3, 4, 5, 6]' is not assignable to parameter of type 'readonly [string, ...string[]]'.
      Type at position 0 in source is not compatible with type at position 0 in target.
        Type 'number' is not assignable to type 'string'.
  Overload 2 of 2, '(values: [string, ...string[]]): ZodEnum<[string, ...string[]]>', gave the following error.
    Argument of type '[1, 2, 3, 4, 5, 6]' is not assignable to parameter of type '[string, ...string[]]'.
      Type at position 0 in source is not compatible with type at position 0 in target.
        Type 'number' is not assignable to type 'string'.ts(2769)

Is there any reason why z.enum only expects strings?

https://codesandbox.io/s/zod-number-union-4pqezv

amatiasq avatar May 05 '22 18:05 amatiasq

This would be very useful for us at Ping

t3dotgg avatar Jun 08 '22 20:06 t3dotgg

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 Aug 07 '22 22:08 stale[bot]

Well it is staled but it's not "completed" :\

amatiasq avatar Aug 15 '22 13:08 amatiasq

For anyone else running into this problem, this is how I worked around the limitation in z.enum:

import { z } from 'zod';
import type { ZodType } from 'zod';

export const LIMIT_OPTIONS = [10, 25, 50] as const;

export function numericEnum<TValues extends readonly number[]>(
  values: TValues,
) {
  return z.number().superRefine((val, ctx) => {
    if (!values.includes(val)) {
      ctx.addIssue({
        code: z.ZodIssueCode.invalid_enum_value,
        options: [...values],
        received: val,
      });
    }
  }) as ZodType<TValues[number]>;
}

const TestSchema = z.object({
  property: numericEnum(LIMIT_OPTIONS),
});

export type Test = z.infer<typeof TestSchema>;

Playground link

joshuat avatar Sep 02 '22 04:09 joshuat

@colinhacks Would you be open to a PR to add number support to enums? I know it isn't a 1:1 with the TypeScript definition of an enum, but this is in practice much more akin to a union (that's even what it infers to) than it is an enum in the TypeScript sense.

joshuat avatar Dec 13 '22 17:12 joshuat

Hey, would be nice to have that

sylvainbaronnet avatar Oct 11 '23 08:10 sylvainbaronnet

I think we can at least re-open this issue

sylvainbaronnet avatar Oct 11 '23 08:10 sylvainbaronnet

A work around is like z.union([z.literal(-1), z.literal(1)])

sgpinkus avatar Nov 04 '23 10:11 sgpinkus

any official updates on this?

stormynight9 avatar Dec 10 '23 08:12 stormynight9

I think "Native enums" might help: https://zod.dev/?id=native-enums

Sample code:

import { z } from "zod";

const Multipliers = {
    one: 1,
    two: 2,
    three: 3,
    four: 4,
} as const;
const schema = z.nativeEnum(Multipliers);

Moustafa3attia avatar Jan 16 '24 10:01 Moustafa3attia