Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Zod nativeEnum type checks enum's value

Tags:

zod

typescript

I am using a zod schema to validate an object with an enum field in it:

enum Colour {
    red: 'Red',
    blue: 'Blue',
}

const schema = z.object({
    colour: z.nativeEnum(Colour),
});

I have input data coming from an api for the colour values as either 'red' or 'blue', and I want to check this with the above schema. However, the above schema's nativeEnum checks according the capitalized cases in the enum, not the enum properties:

enum Colour {
    red: 'Red',
    blue: 'Blue',
}

const schema = z.object({
    colour: z.nativeEnum(Colour),
});

const rawInput1 = {
    colour: 'red' // should be identified as valid
};
const parsedInput1 = schema.parse(rawInput1); // this fails

const rawInput2 = {
    colour: 'Red' // should be identified as invalid
};
const parsedInput2 = schema.parse(rawInput2); // this passes

How can I make zod validate based the property in the enum instead of the value? And why is this happening?

The reason why I also want to parse the enum properties and I have defined the enum that way is because I want to parse the object and use the colour variable to index its string value in the enum: Colour[parsedInput1.colour]. This will not be possible if colour is the string value.

like image 332
Lemour Avatar asked Oct 20 '25 03:10

Lemour


1 Answers

You could get it to validate based on the keys using a regular .enum() and extracting the enum keys with Object.keys():

enum Colour {
    red = 'Red',
    blue = 'Blue',
}

const keys = Object.keys(Colours) // ["red", "blue"]

const schema = z.object({
    colour: z.enum(keys),
});

But the issue is that the type of keys here is string[] and not ["red", "blue"] (even though this will be the actual value at runtime). So it will work, but you won't get the typescript validation...

With keyof typeof it is possible to obtain a unions of the enum's keys

type KeyUnion = keyof typeof Colour // "red" | "blue"

So with a little bit of "lying to typescript" we can achieve the desired result by casting the keys type:

const keys = Object.keys(Colours) as [keyof typeof Colour]
const schema = z.object({
    colour: z.enum(keys),
});

This gets you something that works both at the type level and at runtime.

However do note that keys is not actually of type [keyof typeof Colour], this is just a lie we tell typescript to obtain the correct inference.

like image 79
Sheraff Avatar answered Oct 26 '25 16:10

Sheraff