-
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
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
No .partial(), .deepPartial(), .merge() and others after using .refine() on ZodObject #597
Comments
You can find some additional details and context in the RFC about v3 |
@kevlugli This also comes up a lot in the context of container types like |
Yeah, I've read it before but I think it applies more to transformers than refinements.
Yep, that's actually true. In the case I was facing, I wanted to do this // in some file
export const credentialDataSchema = object({
firstName: string().nonempty(),
lastName: string().nonempty(),
birthday: date(),
countryId: string(),
zipCode: zipCodeSchema().nullish(),
}).refine(
(data) => {
const isUs = data.countryId === 'US';
const isEmpty = data.zipCode === undefined || data.zipCode === null;
return !((isUs && isEmpty) || (!isUs && !isEmpty));
},
{
message: 'zipCode should be complete when countryId is US',
path: ['zipCode'],
},
);
// in another file
// in v1 done with .merge() and not .and()
export const userDataSchema = credentialDataSchema.and(
object({
email: string().email(),
phone: string(),
}),
);
// in yet another file
export const credentialEditionDataSchema = credentialDataSchema.partial()
export const userEditionDataSchema = userDataSchema.partial() That's doable with Typescript's types |
Anyway I think it would be worth cosidering supporting that kind of behavior. Maybe with an external function like mine (but more polished), that allows to apply functions to the internal schemas. It should support ZodOptional and ZodNullable also. |
Your example demonstrates the difficulty of a general solution. credentialEditionDataSchema.parse({
firstName: "Scott",
lastName: "Trinh",
zipCode: "100001",
birthday: new Date(2030, 0, 1),
}); In your particular refinement's case, you're testing I know it's annoying to have to respecify refinements, but I think if you end up transforming a type that you also need to refine, you'll want to maybe share refinements that are defensive and applicable to all of the possible input values. const usZipcodeRefinement = (data: { countryId?: string; zipCode?: string | null }) => {
const isUs = data.countryId === 'US';
const isEmpty = data.zipCode === undefined || data.zipCode === null;
return !((isUs && isEmpty) || (!isUs && !isEmpty));
}; Then you can export an unrefined version that you can call |
Yeah that's a good point. I did know it was going to support an optional zipCode but in the schema is not evident. I've already considered and tried the solution you suggested, but the derived |
Yeah, I think the only way to avoid that is to use naming conventions and comments like: // Note: this schema is unchecked for business logic refinement
export const unsafeCredentialDataSchema = z.object({
// ...
});
export const credentialDataSchema = unsafeCredentialDataSchema.refine(usZipcodeRefinement); |
+1 to Scott's explanation and workaround. This is by design. |
The
refine
method on a ZodObject instance returns a ZodEffects instance which lacks any of the object methods. It broke some code after migrating to v3.I don't know the reasons behind this change, I guess it's to support chaining multiple
refine
andtransform
calls, but maybe there's a way to keep the original methods.It should also benefit another schema types' methods.
The text was updated successfully, but these errors were encountered: