-
-
Notifications
You must be signed in to change notification settings - Fork 1.5k
ZodObject that are .refine
or .superRefine
returns a ZodEffects type
#2474
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
Comments
.refine
or .superRefine
returns a ZodEffects type.refine
or .superRefine
returns a ZodEffects type
You should use |
@thibaultleouay |
This is the expected behavior. The unexpected behavior needs to be documented. I don't see how |
I solved this by adding a wrapper object around the actual object to validate. This way it is possible to use refine on nested objects while returning a type compatible with ZodTypeAny. const schema2 = z.object({ Then add a wrapper middleware to wrap the post data before you parse it and add some custom unwrapper to get the proper form field errors. |
It also prevents the use of |
Is this what you are looking for? const schema1 = z.object( { foo: z.string() } )
const schema2 = z.object( {
bar: z.string(),
attributeRelatedToBar: z.string(),
} )
const finalSchema = schema1.merge( schema2 ).superRefine( handleSchema2Errors )
schema2.shape.bar // z.ZodString
schema2.shape.attributeRelatedToBar // z.ZodString If you found my answer satisfactory, please consider supporting me. Even a small amount is greatly appreciated. Thanks friend! 🙏 |
Is this what you are looking for? The schema is a validation schema and must be fully functional. So in my context, this is a necessity to have a There is a problem between Even if I wanted to use a workaround, the solution you suggested would have been easy to find. |
How is what I gave you not "fully functional"?
I used
I fail to see the problem that you are talking about. Please explain more.
Considering Zod doesn't release very often, your choices are to use a workaround in the mean time or wait for your problem to be addressed and then wait for it to be released. Based on how things have been going, that could be 6 months. If that's ok for you, then great.
Generally when someone tries to help you, it's not the best approach to belittle their efforts. But thanks. |
.refine
or .superRefine
returns a ZodEffects type.refine
or .superRefine
returns a ZodEffects type
I am working on a dynamic form that can change depending on the persona. So I've created a schema by input, which is then assembled according to the displayed input.
Yes, but at the end of the final schema and if I need to merge multiple object, what the method refine receive in parameter will change according to what it's merged inside. Not easy to maintain.
To be more concrete, say I have a WYSIWYG input and I need to count how many characters are written (without taking into account html tags). To do that, I need to use
I have another workaround on the validation side after a
Sorry, I didn't mean to be mean, but from the way I described the bug, you can imagine that I'm not a junior. The proposed solution wasn't very complex and the reason for this issue was a real problem with the library. |
Seems that |
This will break when you pass it a schema that uses refine or superRefine, the resulting type ZodEffects does not extend ZodTypeAny (that constraint is required). |
Up on this topic ? Issue still the same with zod |
This unfortunately make complex types into a real pain to use with this library |
I came across as well. If refine shouldn't mean to support this, what does so? |
I am running into the same issue but after using const conversionFn = (val) => val === null ? undefined : val;
const parentSchema = z.object({
// Accepts `number | null | undefined` but returns `number | undefined`
numberProp: z.number().nullable().optional().transform<number | undefined>(conversionFn),
});
const childSchema = parentSchema.required({ numberProp: true }); Because my It is also a similar problem when using |
I'm running into issues as well. It doesn't make sense to me for the object to transform itself into a different type if
|
What not just apply your |
@oyatek It breaks the composition Example 1Let's imagine that we have a reusable type utility: // utils/reusableSchema.ts
export const activeDateTimes = z
.object({
activeFromDateTime: z.string().datetime().optional(),
activeToDateTime: z.string().datetime().optional(),
})
.superRefine((data: ActiveDateTimes, ctx) => {
if (
data.activeFromDateTime &&
data.activeToDateTime &&
new Date(data.activeFromDateTime) > new Date(data.activeToDateTime)
) {
ctx.addIssue({
path: ['activeToDateTime'],
code: z.ZodIssueCode.custom,
message: 'Active to date should be after active from date',
});
ctx.addIssue({
path: ['activeFromDateTime'],
code: z.ZodIssueCode.custom,
message: 'Active from date should be before active to date',
});
}
}); And you want to reuse it in multiple places: const userSchema = z.object({name: z.string}).merge(activeDateTimes)
const postSchema = z.object({title: z.string}).merge(activeDateTimes)
... The current approach makes it difficult to implement a schema utility and, in some ways, breaks schema encapsulation. Example 2You have some schema that represent API endpoint const user = z.object({
id: z.string(),
name: z.string(),
age: z.string()
}).refine(...) But for the API POST method, you want to modify your schema and omit const createUserSchema = user.omit({id: true}); You may have noticed that there is an issue with the current implementation of your application. Specifically, you may need to refine some of the schemas during the latest stage of development. However, the proposed solution would require you to rewrite a large portion of your application. |
Same issue here! export const ignoreDateTypeValidation = <Schema extends z.AnyZodObject>(schema: Schema) => {
const entries = Object.entries(schema.shape) as [keyof Schema['shape'], z.ZodTypeAny][]
const newProps = entries.reduce(
(acc, [key, value]) => {
acc[key] = value instanceof z.ZodDate ? z.any() : value
return acc
},
{} as { [key in keyof Schema['shape']]: Schema['shape'][key] },
)
return z.object(newProps)
} the issue is that the date validation does't get picked up because it is using export const pastDateSchema = z.date().refine((d) => d.getTime() <= Date.now(), { message: 'cannot use future dates' }) but I cannot use acc[key] = value instanceof z.ZodEffects ? z.any() : value The same goes if I use Is there any way for me to pick up the validation type even after using refine? |
Same issue here - I'm trying to add/replace a part of a schema to avoid duplicating the code dozens of times: export const specSchema = z.object({
value: z.string(),
project: projectSchema,
});
export const globalSchema = z.preprocess(
(val: any) => {
const { kind, name, displayName, ...other }: { [key: string]: any } = val;
return {
kind,
metadata: { name, annotations: { displayName } },
spec: other,
};
},
z.object({
kind: z.string().optional(),
metadata: metadataSchema,
spec: specSchema,
})
); but I can't use extend or merge to do that... (I tried handing in the specSchema via function parameter, ending up with a valid schema, that is missing the type details about |
This isn't only an issue for the object schema. It is an issue for all schemas.
|
Making these changes to the repo fixes the core issue I haven't made a PR because it still has type conflict errors and is failing a lot of unit tests. |
Has anyone managed to resolve this? I have this problem, I use .refine and now I want to use .pick but it doesn't exist in ZodEffect. |
I've stumbled upon this as well... I create a super refiner programmatically, so I don't really have access to the base schema:
what about this? (I just discovered
Does it suck? It that intended to be used this way? |
@KikoCosmetics innerType() seems to remove the refine logic, at least for me it is no longer running. So no, this is not a solution. |
This will be changed in Zod 4 — refinements will be stored in the schema itself instead of in an enclosing |
In my scenario it is a solution, cause I only picked a field that is not refined. But yes if I had to pick the passwords I should refine them again. Not very cool, but that’s essentially the reason why I created that ‘passwordMatchRefiner’ function… |
@colinhacks Do you have any updates on when Zod 4 might be done? The current version has been great, but it sounds like Zod 4 will be a huge improvement. |
Struggling with the same problem... Waiting for Zod 4. Now its a bit painfull to reset another field if other is false. |
I was hoping to get a workaround here, I have been battling this issue all day long, hopefully Zod 4 will be released soon. |
Zod 4! We are waiting..! |
I have the same issue. I have a schema where I all Any timeline on the release of v4? |
I have the same issue. This is my use case: I want to transform a field to have other sibling value but const fooAndBar= z
.object({
foo: z.string(),
bar: z.string().nullish(),
})
.transform((val) => {
return { ...val, bar: val.bar ?? val.foo};
});
const otherSchema= z
.object({
...
})
// Fails! fooAndBar is z.ZodEffect and cannot be used
.merge(fooAndBar); The same happens if I use |
any news on Zod v4 ? |
@matomesc same problem for me, did you guys find any solution to this? Is v4 going to solve the issue? I actually need to extend my zod schemas and not being able to use .omit after refine is really troublesome. |
Due to an issue with |
Hey any updates on this or reliable to handle this cases? |
Hey how are people actually solving this issue? seems like something almost everyone will experience... |
This is fixed in Zod 4. See the announcement. |
Uh oh!
There was an error while loading. Please reload this page.
Zod version
3.21.4
Problem
Using
.refine()
or.superRefine()
onZodObject
(z.object()
) prevents the use of.shape
or.merge()
method as the returned type is aZodEffects
and not aZodObject
.Example
Expected behavior
Using
.superRefine()
or.refine()
onz.object()
should return aZodObject
type– or –
.merge()
&.shape
should be functional onZodEffects
applied to aZodObject
.The text was updated successfully, but these errors were encountered: