tozod
tozod copied to clipboard
How might this support .transform()?
Here's an example:
const stringToNumber: toZod<string> = z.string().transform((val) => val.length);
const a = stringToNumber.parse('string'); // => 6
It'd be nice to know that the value passed to the schema WAS a string, but that the parsed object would return a number? Have you run into this?
My use case is that:
- All my endpoints that take dates (in queries or request bodies) hit my backend as strings.
- I'd like to .refine() the schema to make sure that they are ISO strings even
- But after parsing with zod I'd love to use Date objects again.
So this is my example:
const stringToNumber: toZod<string> = z.string().transform((val) => new Date(val));
const a = stringToNumber.parse('2022-01-01'); => // Date('2022-01-01')
All my endpoints that take dates (in queries or request bodies) hit my backend as strings.
Same problem. It does not seem like there is a solution for this.
I did not end up using toZod. Here's my schema:
export const ZCoerceDate = z.any()
.superRefine((v, ctx) => {
if (!v) {
ctx.addIssue({
code: 'custom',
params: {
customCode: 'required'
},
message: 'Required',
})
}
})
.transform((v: any, ctx) => {
if (typeof v === 'number') {
v = v.toString()
}
const day = dw(v);
if (!day.isValid()) {
ctx.addIssue({
code: 'custom',
params: {
customCode: 'could-not-parse'
},
message: 'Not a valid date, ISO date string, or YYYY-MM-DD formatted string',
received: v
});
}
return day.toDate();
});
In the case above dw
is really just a wrapper around dayjs
.
I ended up using toZod
when it works, but when things are too complicated and it fails, I have the following helper function:
/** Makes sure two types are equal */
export function TypesAreEqual<T,U>(trueOrFalse: T extends U ? U extends T ? true : false : false) {
return trueOrFalse ? true : false
}
It doesn't tell me what is not compatible, but at least it tells me if they're incompatible. For example:
export type Client = {
client: string;
score: number;
}
export const ClientSchema = z.object({
client: z
.string()
.uuid()
.transform((uuid) => uuid.toLowerCase())
.refine((uuid) => uuid !== '00000000-0000-0000-0000-000000000000', { message: 'Client is required' }),
score: z.number().min(1, { message: 'Score must be greater than 0' }),
})
// This will not compile if they don't match: Argument of type 'true' is not assignable to parameter of type 'false'
TypesAreEqual<Client,z.infer<typeof ClientSchema>>(true)