-
-
Notifications
You must be signed in to change notification settings - Fork 1.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[PoC] Add refineWith #420
[PoC] Add refineWith #420
Conversation
Here's some more background on this: consider that you're using Zod to parse URL query params, which are defined as optional strings, and transform them into some usable type (e.g., enum, boolean, integer, etc.). Specifically, let's consider a param that represents the limit on the number of records that should be returned from a query endpoint. We'd currently need to write that like this: z
.string()
.transform(v => v ? +v : undefined)
.refine(v => v === undefined || Number.isInteger(v), {message: 'should be an integer'})
.refine(v => v === undefined || (v >= 0 && v <= 100), {message: 'should be between 0 and 100'})
.optional() Instead, I'd love to be able to write: z
.string()
.transform(v => v ? +v : undefined) // Treat an empty string as undefined.
.refineWith(z.number().int().min(1).max(100).optional())
.optional() This could be used for more complex validations too, such as transforming a comma-separated string of enums into an actual array of enums more elegantly (i.e., using |
See also: #345 |
As an alternative to this, if |
Could also do both, e.g.:
|
): This extends ZodEffects<infer T, any> | ||
? ZodEffects<T, NewOut> | ||
: ZodEffects<This, NewOut> { | ||
let parsed: ReturnType<typeof returnType.safeParse>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually I suppose there's a concurrency bug here due to this being in the scope of the schema rather than the specific parsing invocation of it. So parsed
would have to be stashed somewhere within the context.
I stumbled across this pull request while searching for a solution to the same problem. Until this is merged, I am using this function: /**
* Transform the result of a zod scheme to another type and validates that type against another zod scheme.
* The result of this function basically equals inputSchema.transform((val) => outputSchema.parse(transformer(val))),
* but errors thrown during the transformation are handled gracefully (since at the moment, zod transformers
* do not natively support exceptions).
*/
export function transformValidator<Output, Input1, Input2, Input3>(inputSchema: z.ZodType<Input2, any, Input1>, transformer: (input: Input2) => Input3, outputSchema: z.ZodType<Output, any, Input3>): z.ZodEffects<z.ZodEffects<z.ZodType<Input2, any, Input1>, Input2>, Output> {
// For now we have to parse the schema twice, since transform() is not allowed to throw exceptions.
// See https://github.com/colinhacks/zod/pull/420
return inputSchema.superRefine((val, ctx) => {
try {
const result = outputSchema.safeParse(transformer(val));
if (!result.success) {
for (const issue of result.error.errors) {
ctx.addIssue(issue);
}
}
} catch (err) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: err.message,
});
}
}).transform((val) => outputSchema.parse(transformer(val)));
} I'm using it for example to transform a JSON string to an object and then validating that object against a schema: const jsonValidator = transformValidator(z.string().optional(), (val) => val ? JSON.parse(val) : undefined, z.object({
...
}); |
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. |
Adding a helper method that can be used to refine a value with a specified schema, in order to take advantage of Zod's built-in refinement functionality. This can be useful for refining a list of strings to a list of enums, for instance, or enforcing bounds on a number.