Zod Tutorial (10 exercises)
solution

Refactoring Duplicated Schemas

Once again, there are a bunch ways that this code could be refactored.

For reference, here's 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(),
});

The Simple solution

The simplest solution is to strip out the id into its own 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(),
});

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

Use the Extend Method

Another solution would be to create a base object which we'll call ObjectWithId. This base object will contain our id:

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

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

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

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.

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!

Transcript

There are a bunch of possible solutions here. The simplest solution is to strip out the ID thing here into its own type and then reference that within all the other z.objects, which is pretty good but we're still getting this object id: Id thing here. All of our cases are passing. This is OK.

Another solution would be to create a base object here, which is an ObjectWithId and then use the .extend function. What extend does is it creates a new type with the initial stuff here and the stuff that you add here.

What we end up with is we've got this ObjectWithId. That's the base of all of these pieces. If we add something else here, like created At which could be z.date, then this would also be shared across of the objects. That, obviously, wasn't the ID because we're going to get a lot of errors, then.

Another way you could do this is by using merge here. Merge, what it means, is you take the base ObjectWithId and you merge it with another object. You can see that this is slightly more verbose than the other version, then just extend, because here we're we get to pass in an object containing the name z.string.

Inside here, we actually have to pass a z.object. We have to wrap it in Zod. What this means is you can use this more generally to merge two different types together instead of just extending from them.

Those are 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.