zod icon indicating copy to clipboard operation
zod copied to clipboard

Enums should support an omit method

Open jasonkuhrt opened this issue 2 years ago • 12 comments

Example of what has to be done right now:

const a = z.nativeEnum( { a: 1, b: 2 } as const )
const b = z.nativeEnum( Remeda.omit( a._def.values, [ 'a' ] ) )

b._def.values.b // works
b._def.values.a
//            ^
// Property 'a' does not exist on type 'Omit<{ readonly a: 1; readonly b: 2; }, "a">'.

I'm not sure how to do it with zod enums actually.

This issue would be asking for native and zod enums though to support an omit method.

jasonkuhrt avatar Feb 25 '22 03:02 jasonkuhrt

Here's how you do it with zod enums

const foo = z.enum( [ 'a', 'b' ] )

type ExcludeA = Exclude<z.infer<typeof foo>, 'a'>
// type ExcludeA = "b"

const bar = z.enum(
    foo.options.filter( x => x !== 'a' ) as [ ExcludeA, ...ExcludeA[] ]
)

bar.enum.b // works
bar.enum.a
//       ^
// Property 'a' does not exist on type 'Values<["b", ..."b"[]]>'.

JacobWeisenburger avatar Feb 25 '22 16:02 JacobWeisenburger

There's an argument that this should be a top-level z.omit utility, which has been suggested elsewhere.

const a = z.nativeEnum( { a: 1, b: 2 } as const )
const omitted = z.omit(a, z.literal(2));

Once we have a ZodEmit schema type, we can add a global .omit method on the base ZodType.

colinhacks avatar Mar 01 '22 20:03 colinhacks

@colinhacks in your example you're omitting by value?

Once we have a ZodEmit schema type, we can add a global .omit method on the base ZodType.

How about generic static .omit function AND method helpers too?

jasonkuhrt avatar Mar 02 '22 03:03 jasonkuhrt

you're omitting by value?

The inferred type of this this enum is 1 | 2 so you'd omit a literal 2 to remove that option.

const a = z.nativeEnum( { a: 1, b: 2 } as const )
// 1 | 2

You could also do this:

const a = z.nativeEnum({ a: 1, b: 2 } as const);
const omitted = z.omit(a, z.literal(a.values.b));

AND method helpers too?

Yep that's what I mean by "add the .omit method on the base ZodType". This would add the .omit method to all Zod schemas.

colinhacks avatar Mar 02 '22 19:03 colinhacks

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 May 02 '22 17:05 stale[bot]

Pleae re-open

jasonkuhrt avatar May 02 '22 17:05 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 Jul 01 '22 18:07 stale[bot]

Pleae re-open

jasonkuhrt avatar Jul 05 '22 14:07 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 Sep 03 '22 15:09 stale[bot]

Pleae re-open

jasonkuhrt avatar Sep 03 '22 21: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 02 '22 21:11 stale[bot]

Please re-open

jasonkuhrt avatar Nov 03 '22 03:11 jasonkuhrt

I was able to get this working using .refine. Of course it could be easier, but it was good enough for my solution.

Typescript Playground

import { z } from 'zod';

enum Status {
  todo = 'todo',
  inProgress = 'inProgress',
  completed = 'completed',
}

const notCompleted = z
  .nativeEnum(Status)
  .refine((status): status is Exclude<Status, Status.completed> => status !== Status.completed);

type NotCompleted = z.infer<typeof notCompleted>;
     // ^? type NotCompleted = Status.todo | Status.inProgress;

deriegle avatar Jan 31 '23 20:01 deriegle

ZodEnum now has a .exclude() method that implements this behavior as of Zod 3.21.

Top level z.omit is probably not going to happen so I'm going to close.

colinhacks avatar Mar 07 '23 22:03 colinhacks

Would it be possible to add the .exclude() method also in ZodNativeEnum?

I have a case where I need to exclude from a prisma enum a specific value:

const validationSchema = z.object({
  ...
  type: z.nativeEnum(Prisma.AutomationType),
  ...
});

Currently I can recreate the native enum schema with a ZodEnum, and then call .exclude() on it, but my AutomationType would grow huge so I would like a simpler approach.

nullndr avatar Apr 17 '24 14:04 nullndr