External Libraries 9 exercises
solution

Infer Runtime Arguments from a Zod Schema

Step 1: Add Generics

Let's start by thinking about the type arguments for the generic slots.

We need to infer the type of the argument and the return type of the makeZodSafeFunction, so let's start by adding two generic slots for TValue and TResult:

const makeZodSafeFunctio
Loading solution

Transcript

0:00 Let's start by thinking about the type arguments, the generic slots. We've got this makeZodSafeFunction. We know we're definitely going to need to infer the type of the argument and the type of the return type of this function. Let's add two generic slots here.

0:14 We can go TArg and then TResult. What this is going to do, we can slot that in there, and then we can slot this in here. This means then that this arg is also going to be typed as TArg, probably, because when we get down to here, we should get a type error when we call addTwoNumbers.

0:36 Let's add this in. Let's go TArg there. This result now, if we take a look at this, it's not like the inference isn't yet working. We can make it work by manually passing in these args here. We can say A Number and then B String, oh sorry, B Number.

0:55 Then what we get is we actually get all of the inference happening for us. We can see that A Number, B Number is being inferred in the first slots, which is this argument here. Then it's also being inferred that args.a, which is a number, plus args.b, which is a number, equals a number.

1:13 We're seeing that we get a number back out. We should see that if we go const result here, for instance, ba ba ba, const result equals this, result, expect result to be three. We're getting number back out of this.

1:28 Whereas if we don't type this yet, we would get any out from this, because it can't work out what unknown.a plus unknown.b should be. This isn't the goal, right? We're expecting to be able to infer the type of args from the schema that we pass in.

1:47 The way you do this with Zod is if we look at Z.ZodType, we know that, imagine if we had Z.ZodObject in this slot. Z.ZodObject, it's actually got quite a lot of generics in here. It's got a raw shape, unknown keys, catch all, output. Output looks like the one that we actually want.

2:08 Then there's input too. We've seen output and input on the Zod, on the ZodType, which we saw in the previous exercise. We could pass in a few things here. We could pass in any, any, any, and then is it the fourth one, I think? God. One, two, three, four is the output.

2:26 We can add in TArg here. Actually, this works, or it works rather for objects. Now we can actually remove this definition and we'll get args.a and args.b working for us. That's really, really cool.

2:44 We now can't pass in, for instance, if we had Z.Number, this actually doesn't work because it's expecting a Zod object in that slot. This is kind of no dice for us. We actually need to reference something that can be any ZodType or any Zod schema. Luckily, we know one.

3:02 We know that Z.ZodType. If we just put in TArg as the first one, because Z.ZodType actually starts with the output instead of it being in the fourth slot, this will work. Now we can pass in a number if we want to. We have Z.Number. Now this is inferred as a number.

3:22 What this means is you can use Zod in this way to basically say OK, I'm going to take in a schema and I want you to infer the type of that schema and then use that type further down the line inside this function. Or imagine if we were building something with Express, for instance.

3:39 You could take that TArg and pass it into the query params or pass it into rec.body. It's so cool. This means that you can sit at the, Zod can sit at a really nice high level in your app. Again, we're creating a function here that needs no type annotations. It just flows together and it works.

3:59 Finally then, we can actually alias this to something that looks really nice, which is there's a Z.Schema or Z.Zod schema, which Zod just exposes. You can see that actually ZodSchema. There it is. It actually exports that as an alias. ZodType as schema, ZodType as ZodSchema.

4:19 If you want to make it nice and pretty. I personally like Z.Schema. I think that's really nice.

4:24 This is such a cool way to work because it means that you're wrapping your stuff with, if you're using Zod in your app anyway, and you need some runtime safety, then you can also get type safety inferred from those schemas.