zod icon indicating copy to clipboard operation
zod copied to clipboard

v4: No overload matches this call: `z.enum({readonly['foo','bar']})`

Open RayGuo-ergou opened this issue 8 months ago • 4 comments

When using z.enum with a constant array, tsc throws a no overload matches error.

z.enum(['foo', 'bar']) // Work correctly ✅

const foo = ['foo', 'bar']
z.enum(foo) // work correctly and infer type as string ✅

const foo = ['foo', 'bar'] as const
z.enum(foo) // throws No overload matches this call ❌

Image

    "zod": "4.0.0-beta.20250410T230029"
//tsconfig.json
{
  "compilerOptions": {
    "target": "ESNext",
    "module": "ESNext",
    "moduleResolution": "Bundler",
    "noEmit": true,
    "esModuleInterop": true,
    "forceConsistentCasingInFileNames": true,
    "resolveJsonModule": true,
    "strict": true,
    "skipLibCheck": true,
    "paths": {
      "@": [".."],
      "@/*": ["../*"],
      "~": [".."],
      "~/*": ["../*"],
      "@@": [".."],
      "@@/*": ["../*"],
      "~~": [".."],
      "~~/*": ["../*"]
    }
  }
}

Wondering if this the right change to it

- declare function _enum<const T extends string[]>(values: T, params?: core.$ZodEnumParams): ZodEnum<util.ToEnum<T[number]>>;
+ declare function _enum<const T extends (string[] | readonly string[])>(values: T, params?: core.$ZodEnumParams): ZodEnum<util.ToEnum<T[number]>>;

RayGuo-ergou avatar Apr 11 '25 00:04 RayGuo-ergou

I think this is a (not ideal) workaround:

const Foo = z.union([z.literal("foo"), z.literal("bar")]);
const foo = Foo.options.flatMap((option) => Array.from(option.values));

martijnarts avatar Apr 11 '25 15:04 martijnarts

Fwiw I think z.enum(['foo', 'bar']) doesn't actually work correctly:

const foo = z.enum(["a", "b", "c"]);
type x = z.infer<typeof foo>; // string

martijnarts avatar Apr 11 '25 15:04 martijnarts

I’ve just had the same problem:

import { z } from 'zod';
const OUTPUT_FORMAT_KEYS = ['html', 'epub', 'pdf'] as const;
const OutputFormatKeySchema = z.enum(OUTPUT_FORMAT_KEYS); // ❌ compiler error

IINM, replacing string[] with readonly string[] in the parameter of z.enum() is the way to go: The latter also accepts the former.

@martijnarts This does work for me (Zod 4 beta):

const abc = z.enum(["a", "b", "c"]);
type X = z.infer<typeof abc>;
  // type X = "a" | "b" | "c"

rauschma avatar Apr 12 '25 11:04 rauschma

Oh interesting that it did work for you. Maybe a similar cause as #4082

martijnarts avatar Apr 12 '25 11:04 martijnarts

Fixed in latest betas. Note that you'll need to rewrite some imports after upgrading per https://github.com/colinhacks/zod/pull/4364

colinhacks avatar May 15 '25 02:05 colinhacks