How to Create a Type from a Zod Schema
A recipe on how to infer a TypeScript type from a Zod schema, keeping your types in sync with your validation.
When I use Zod to define my data schemas, like in my Astro content.config.ts
,
I often need a corresponding TypeScript type for use in my components. Manually
defining the type is a pain because it means I have two sources of truth to keep
updated.
The best way to handle this is to generate the TypeScript type directly from the Zod schema.
Define Your Zod Schema
First, I have my Zod schema defined. For this example, I’ll use the Sidebar
schema from my content.config.ts
, which is used for my project and cookbook
pages.
import { z } from "astro:content";
const SidebarLink = z.object({
type: z.literal("Link"),
title: z.string(),
href: z.string(),
external: z.boolean().optional()
});
const SidebarGroup = z.object({
type: z.literal("Group"),
title: z.string(),
open: z.boolean().optional(),
items: z.array(SidebarLink)
});
const SidebarItem = z.union([SidebarGroup, SidebarLink]);
const Sidebar = z.object({
title: z.string(),
items: z.array(SidebarItem)
});
Infer the TypeScript Type
With the schema defined, I can use z.infer
to create a TypeScript type. This
is how I create a Sidebar
type that I can use in my Astro components.
export type Sidebar = z.infer<typeof Sidebar>;
Use the Inferred Type
Now I have a Sidebar
type that is always in sync with my Zod schema. I can
import and use this type in any of my .astro
or .ts
files.
For example, in a layout component like src/layouts/MinimalLayout.astro
, I can
use it to type the props.
---
import type { Sidebar } from "../content.config.ts";
interface Props {
sidebar: Sidebar;
}
const { sidebar } = Astro.props;
---
<!-- Now I can use `sidebar` with full type safety -->
<nav>
<h2>{sidebar.title}</h2>
...
</nav>
This way, if I ever update the Sidebar
schema in content.config.ts
,
TypeScript will immediately tell me if I need to make changes in my components.
It’s a simple way to keep my validation and types connected.