Published on

Creating Zod Schema for Generic Interface

Authors
  • Name
    Ripal & Zalak
    Twitter

Creating a Zod schema for a generic interface is a common requirement when dealing with runtime type checks. In this guide, we’ll explore how to define a Zod schema for a generic PaginatedResponse interface.

The Problem

You have a TypeScript generic interface for a paginated response:

export interface PaginatedResponse<T> {
  pageIndex: number
  pageSize: number
  totalCount: number
  totalPages: number
  items: Array<T>
}

Now, you want to create a Zod schema for runtime validation. However, the challenge lies in defining the items array with a type that depends on T.

Solution: Generic Zod Schema

Step 1: Create a Generic Function

You can use a generic function to generate a schema for PaginatedResponse based on the schema for the items field. Here’s how you can do it:

import { z } from 'zod'

function createPaginatedResponseSchema<ItemType extends z.ZodTypeAny>(itemSchema: ItemType) {
  return z.object({
    pageIndex: z.number(),
    pageSize: z.number(),
    totalCount: z.number(),
    totalPages: z.number(),
    items: z.array(itemSchema),
  })
}

Step 2: Use the Function to Define a Schema

For example, if T is a string, you can create the schema as follows:

const stringPaginatedResponseSchema = createPaginatedResponseSchema(z.string())

You can infer the TypeScript type from this schema:

export type StringPaginatedResponse = z.infer<typeof stringPaginatedResponseSchema>

Step 3: Support Other Types

You can use the same function to create schemas for other types. For example:

Schema for a User Object

const userSchema = z.object({
  id: z.number(),
  name: z.string(),
})

const userPaginatedResponseSchema = createPaginatedResponseSchema(userSchema)

export type UserPaginatedResponse = z.infer<typeof userPaginatedResponseSchema>

Schema for a Custom Type

const customTypeSchema = z.object({
  key: z.string(),
  value: z.any(),
})

const customPaginatedResponseSchema = createPaginatedResponseSchema(customTypeSchema)

export type CustomPaginatedResponse = z.infer<typeof customPaginatedResponseSchema>

Key Benefits

  1. Reusability: The generic function can handle any type of items field.
  2. Type Safety: Leverage TypeScript’s type inference with Zod.
  3. Runtime Validation: Ensure data integrity with Zod’s runtime checks.

FAQ

Q1: Can I infer a type directly from the schema?

Yes, use z.infer<typeof schema> to derive the TypeScript type from your Zod schema.

Q2: How do I customize error messages?

You can pass a message property to Zod methods like z.number() or z.array():

z.number({ message: 'Must be a number' })

Q3: Is this approach compatible with React?

Yes, Zod works well with React and other frameworks. Use it to validate API responses or form inputs.