Zod - TypeScript-first schema declaration and validation library #10

nhannguyendevjs

Nhan Nguyen

Posted on December 15, 2023

Zod - TypeScript-first schema declaration and validation library #10

Image description

Reduce Duplicated Code by Composing Schemas

We will be looking at techniques for refactoring working code to remove duplication.

Here we have schemas for User, Post, and Comment:



const User = z.object({
  id: z.string().uuid(),
  name: z.string(),
})

const Post = z.object({
  id: z.string().uuid(),
  title: z.string(),
  body: z.string(),
})

const Comment = z.object({
  id: z.string().uuid(),
  text: z.string(),
})


Enter fullscreen mode Exit fullscreen mode

Notice that id is present in each.

Zod provides us with various ways of composing objects together into different types, allowing us to DRY out our code.

👉 Challenge:

We will use Zod to refactor the above code to remove the id duplication.

👉 Solution:

There are a bunch of ways that this code could be refactored.

For reference, here is what we started with:



const User = z.object({
  id: z.string().uuid(),
  name: z.string(),
})

const Post = z.object({
  id: z.string().uuid(),
  title: z.string(),
  body: z.string(),
})

const Comment = z.object({
  id: z.string().uuid(),
  text: z.string(),
})


Enter fullscreen mode Exit fullscreen mode

The Simple solution:

The simplest solution is to strip out the id into its type. From there, each of the z.objects could reference it:



const Id = z.string().uuid();

const User = z.object({
  id: Id,
  name: z.string(),
})

const Post = z.object({
  id: Id,
  title: z.string(),
  body: z.string(),
})

const Comment = z.object({
  id: Id,
  text: z.string(),
})


Enter fullscreen mode Exit fullscreen mode

This is pretty good, but id: ID is still being repeated. All of the cases are still passing, so that is okay.

Use the Extend Method:

Another solution would be to create a base object called ObjectWithId. This base object will contain our id:



const ObjectWithId = z.object({
  id: z.string().uuid(),
})


Enter fullscreen mode Exit fullscreen mode

From there, we can use the extend method to create new schemas that add on to the base object:



const ObjectWithId = z.object({
  id: z.string().uuid(),
})

const User = ObjectWithId.extend({
  name: z.string(),
})

const Post = ObjectWithId.extend({
  title: z.string(),
  body: z.string(),
})

const Comment = ObjectWithId.extend({
  text: z.string(),
})


Enter fullscreen mode Exit fullscreen mode

Note that .extend() will overwrite fields!

Use the Merge Method:

Similar to the above solution, we could use the merge method to extend the ObjectWithId base object:



const User = ObjectWithId.merge(
  z.object({
    name: z.string(),
  }),
)


Enter fullscreen mode Exit fullscreen mode

Using .merge() is slightly more verbose than .extend(). We have to pass in a z.object() that contains the name z.string().

Merging is generally used when two different types are being combined, rather than just extending a single type.

Summary:

Those are a few different ways that you can compose objects together in Zod to reduce the amount of code duplication, make things more DRY, and make things a bit more maintainable!


I hope you found it useful. Thanks for reading. 🙏
Let's get connected! You can find me on:

💖 💪 🙅 🚩
nhannguyendevjs
Nhan Nguyen

Posted on December 15, 2023

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related