Z
Zod2mo ago
MattIPv4

MattIPv4 - :WaveDoggo: Hey Zod experts. Given a...

:Wave_Doggo: Hey Zod experts. Given an arbitrary Zod schema, is there a way I can strip refinements off any string types?
3 Replies
MattIPv4
MattIPv4OP2mo ago
I have this after some back and forth with Copilot, which seems to work but absolutely does not infer the types correctly:
const removeStringRefinements = (schema: z.ZodTypeAny): z.ZodTypeAny => {
if (schema instanceof z.ZodEffects) {
if (schema._def.effect.type === "refinement" && schema._def.schema instanceof z.ZodString) {
return z.string();
}

return z.effect(
removeStringRefinements(schema._def.schema),
schema._def.effect,
);
}

if (schema instanceof z.ZodObject) {
const shape = schema.shape;
const newShape: Record<string, z.ZodTypeAny> = {};
for (const key in shape) {
newShape[key] = removeStringRefinements(shape[key]);
}
return z.object(newShape);
}

if (schema instanceof z.ZodArray) {
return z.array(removeStringRefinements(schema.element));
}

if (schema instanceof z.ZodUnion) {
return z.union(schema.options.map(removeStringRefinements));
}

if (schema instanceof z.ZodIntersection) {
return z.intersection(
removeStringRefinements(schema._def.left),
removeStringRefinements(schema._def.right)
);
}

if (schema instanceof z.ZodOptional) {
return z.optional(removeStringRefinements(schema.unwrap()));
}

if (schema instanceof z.ZodNullable) {
return z.nullable(removeStringRefinements(schema.unwrap()));
}

return schema;
};
const removeStringRefinements = (schema: z.ZodTypeAny): z.ZodTypeAny => {
if (schema instanceof z.ZodEffects) {
if (schema._def.effect.type === "refinement" && schema._def.schema instanceof z.ZodString) {
return z.string();
}

return z.effect(
removeStringRefinements(schema._def.schema),
schema._def.effect,
);
}

if (schema instanceof z.ZodObject) {
const shape = schema.shape;
const newShape: Record<string, z.ZodTypeAny> = {};
for (const key in shape) {
newShape[key] = removeStringRefinements(shape[key]);
}
return z.object(newShape);
}

if (schema instanceof z.ZodArray) {
return z.array(removeStringRefinements(schema.element));
}

if (schema instanceof z.ZodUnion) {
return z.union(schema.options.map(removeStringRefinements));
}

if (schema instanceof z.ZodIntersection) {
return z.intersection(
removeStringRefinements(schema._def.left),
removeStringRefinements(schema._def.right)
);
}

if (schema instanceof z.ZodOptional) {
return z.optional(removeStringRefinements(schema.unwrap()));
}

if (schema instanceof z.ZodNullable) {
return z.nullable(removeStringRefinements(schema.unwrap()));
}

return schema;
};
Scott Trinh
Scott Trinh2mo ago
Can you describe what you're actually trying to do? You want to keep any other effects like transforms, preprocessing, piping, native string refiners (like .email, .uuid, etc), but remove anything explicitly defined as a refine or superRefine?
MattIPv4
MattIPv4OP2mo ago
I ended up finding a different way around the actual underlying issue I was trying to solve for here, but, to entertain what I was trying to acheive here: I have an arbitrary schema that I have no control over that has some props that're strings but then have refines on them that check they're in a known set of string constants. Ideally I'm looking for a way to stripe those refines off so that those props are just generic strings again. I think the logic I had above was pretty close, but I couldn't wrap my head around the recursive types w/ infers that I'd need to strip the refinements out of the schema type generic.

Did you find this page helpful?