Zod - TypeScript-first schema declaration and validation library #10
Tags: programming, javascript, typescript, zod
Published: 2023-12-15 15:00:00

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/x4t3dhyhw8z7kkt57pt2.jpg)

✨ **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**:

```ts
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(),
})
```

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 **_i_**d duplication.

👉 **Solution:**

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

For reference, here is what we started with:

```ts
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(),
})
```

➖ **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:

```ts
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(),
})
```

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_**:

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

From there, we can use the [extend method](https://zod.dev/?id=extend) to create new schemas that add on to the base object:

```ts
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(),
})
```

> Note that **.extend()** will overwrite fields!

➖ **Use the Merge Method:**

Similar to the above solution, we could use the [merge method](https://zod.dev/?id=merge) to extend the **ObjectWithId** base object:

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

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:
- **Medium:** https://medium.com/@nhannguyendevjs/
- **Dev**: https://dev.to/nhannguyendevjs/
- **Hashnode**: [https://nhannguyen.hashnode.dev](https://nhannguyen.hashnode.dev/)/
- **Linkedin:** https://www.linkedin.com/in/nhannguyendevjs/
- **X (formerly Twitter)**: https://twitter.com/nhannguyendevjs/