5

I have the following zod schema, and in some cases there is a field I would like to omit from the schema entirely. I can't just make it optional. I suspect there is some way do it with zod directly. Is there a way to omit fields or to preprocess the schema in some way?

For example, how I can use this schema without this nested field.

const schema = z.object({
  name: z.number(),
  age: z.number(),
  data: z.array(
    z.object({
      id: z.string().optional(),
      name: z.string().nonempty().optional(),
    })
  )
});

const test = schema.shape.data //. ??? how can I omit the name field? 
type typeTest = z.infer<typeof test>; // just data without name field

How I can omit this nested value?

Souperman
  • 5,057
  • 1
  • 14
  • 39
Ksenia
  • 950
  • 6
  • 24

2 Answers2

10

The minimum change to make that would work is:

const test = schema.shape.data.element.omit({ name: true }).array();

but another option would be to reorganize your schema into a few named parts and use merge to combine them like:

import { z } from 'zod';

const dataSchema = z.object({
  id: z.string().optional(),
  someOtherField: z.number(),
});

const namedSchema = z.object({
  name: z.string().nonempty().optional(),
});

const fullDataSchema = dataSchema.merge(namedSchema);

type Data = z.TypeOf<typeof dataSchema>;
type FullData = z.TypeOf<typeof fullDataSchema>;

The other option using omit on your base data schema type to get a schema without that field and then use typeof on the resulting schema. If you want to use the schemas in different scenarios I recommend giving them names.

import { z } from 'zod';

const dataSchema = z.object({
  id: z.string().optional(),
  someOtherField: z.number(),
  name: z.string().nonempty().optional(),
});

const noNameDataSchema = dataSchema.omit({ name: true });

type Data = z.TypeOf<typeof noNameDataSchema>;

There are pros and cons to either approach but the outcome should be the same. (I personally find myself doing the former more often because I find the code easier to follow)

Souperman
  • 5,057
  • 1
  • 14
  • 39
-1

Correct answer

const schema = z.object({
  name: z.number(),
  age: z.number(),
  data: z.array(
    z.object({
      id: z.string().optional(),
      name: z.string().nonempty().optional()
    })
  )
});

const test = schema.shape.data.element.omit({ name: true }).array(); 
type typeTest = z.infer<typeof test>; 
Ksenia
  • 950
  • 6
  • 24
  • 4
    Hi there, I've edited my answer to better match with your expectations. You're right that I misunderstood which field you were trying to omit and sort of conflated two options. I omitted the outer `name` field in the first part. I think the later part of my answer was still valid, and I also link to docs and add some more explanations and alternatives so I think there's still value in leaving my answer there. Were you aware of the edit option? In this case it feels a bit more sensible to me than duplicating the answer to fix a small misconception. – Souperman May 26 '22 at 08:30