How do i write zod schema for a deeply nested object

Viewed 1187

I have the following data:

{
  "2022-06-01": {
    "09:00am": {
      "time_table_id": 1,
      "job_id": 4,
      "start_working_time": "09:00am",
      "end_working_time": "09:00pm",
      "work_duration": "12:00:00",
      "merchant_name": "Brands Outlet @ The Starling",
      "address2": "Jalan SS 21/37, Damansara Utama",
      "job_name": "Pro hero",
      "job_application_status": "Hired"
    }
  }
}

and this is my schema but it is giving me a zod error.

const apiScheme = z.object({
  Date: z.object({
    Time: z.object({
      date: z.string(),
      time_table_id: z.number().nullable(),
      job_id: z.number(),
      start_working_time: z.string(),
      end_working_time: z.string(),
      // work_duration: z.string(),
      // merchant_name: z.string(),
      // address2: z.string(),
      job_name: z.string(),
      job_application_status: z.string(),
    }),
  }),
});

// -- snip -- (React code):

const data = useMemo(() => {
  if (responseData) {
    const parseData = apiScheme.parse(responseData);
    //    error here ~~~~~~~~~~-^
    if (parseData) {
      return parseData;
    }
  }
  return null;
}, [responseData]);
1 Answers

I think your problem is that you're using the z.object type when you actually want to be using z.record.

const apiSchema = z.record(
  z.string(), // Key type for the outer record
  z.record(
    z.string(), // Key type for the second level record
    z.object({
      time_table_id: z.number().nullable(),
      job_id: z.number(),
      start_working_time: z.string(),
      end_working_time: z.string(),
      work_duration: z.string(),
      merchant_name: z.string(),
      address2: z.string(),
      job_name: z.string(),
      job_application_status: z.string()
    }),
  ),
);

z.object is used in cases like the schema for the inner most object, where all of the field names are known and will remain the same. z.record is used when you have a type like TypeScript's Record type which is just an object that is indexable but the actual keys of the object are not known at compile time.


Small Aside: If you know at compile time that the values are always going to be the same date and time, you could instead write the schema like:

z.object({
  "2022-06-01": z.object({
     "09:00am": z.object({ /* ... */ }),
  },
});

but I am assuming that the "Date" and "Time" indexes are not known at compile time. A schema like the one above should parse the exact data shape in your example as well (but would fail if either of the dates or times were different).

Related