Zod Tutorial (10 exercises)
solution

Be Specific with Allowed Types

There are two possible solutions to this challenge.

One Solution: Use Unions & Literals

In the first solution, we'll use Zod's union method and pass in an array of literals for "private" and "public":

const Form = z.object({
  repoName: z.string(),
  privacyLevel: z.union([z.literal("private"), z.literal("public")]),
});

Literals can be used to represent numbers, strings, or booleans. They cannot represent objects.

We can use z.infer to check our Form type:

type FormType = z.infer<typeof Form>

Hovering in VS Code will show that we have either "private" or "public" as the options for privacyLevel.

An Arguably Cleaner Solution: Enum

Using Zod enums with z.enum does exactly the same thing under the hood:

const Form = z.object({
  repoName: z.string(),
  privacyLevel: z.enum(["private", "public"]),
});

We are able to use sugar syntax to parse literal values instead of having to use z.literal.

This does not create an enum the way you think of it in TypeScript.

In TypeScript we would write:

enum PrivacyLevel {
  private,
  public,
}

Instead, a union type is created.

Using the same "hover to check" technique that we used above, we see the same thing despite the slightly different syntax: "private" or "public" in a string union.

Transcript

Matt Pocock: This is the first solution. We've got z.union. Inside z.union we are passing an array. An array contains two possibilities. It contains a z.literal and another z.literal.

These z.literals, you can use them to represent anything. They represent to you some...Any sort of literal values inside typescripts, rather. You can't use them to represent objects. You can use them to represent numbers or strings or booleans.

What you end up with is if we say type formType = z.infer<typeof Form>, then we end up with private or public here. If I were to add another one to this in z.literal...What's the one down here, something-not-allowed. Then, what you get is this test would fail, of course, because we're specifically saying this thing is not allowed. We've now allowed it inside our union.

The other way to represent this, which is, I'd argue, a bit cleaner, is z.enum. This does exactly the same thing. It's just sort of under the hood. It's a sugar syntax for the other thing. I'm not sure you can parse it in z.literal here. Yeah.

You just parse in the literal values and it creates an enum for you. Rather, it doesn't create an enum as the way you think of it in TypeScript. It doesn't say PrivacyLevel private public like this. It doesn't do this. Instead, it creates a string literal type for you or a union type.

We've got FormType = z.infer<typeof Form>. Here, again, even though we're using a slightly different syntax, it's producing the same thing, private or public in a string union.