Paul: Hi there, and welcome to PodRocket. I'm your host, Paul, and today we're joined with Matt Pocock. So Matt, I know you used to work at Vercel, and kind of phased out, and now you're a content person. Matt Pocock: Yes, I'm a content person. A person of content. Yeah, I was at Vercel for three months this year, which is kind of nuts. Before that I was at Stately, and now I'm working on a TypeScript course and have gone full-time with content creation, which is super strange to say. Paul: Which came first, the course or the Vercel? Because that surprised me. Three months of Vercel is pretty quick, and a course is not a three-month endeavor. Matt Pocock: No, quite. Well, they both started happening around the same time. I went to a conference in Utah called Remix Conf, where I got this nice hoodie I'm wearing, and I met this guy called Joel Hooks there, who's the guy behind egghead. He does a lot of different courses. And he made me a burger, I think, or helped make me a burger, and I kind of liked him. And around the same time I was chatting to Lee Robinson from Vercel and thinking, "Okay, maybe I could join these guys." And so I joined Vercel for three months, three days a week, which is kind of unusual, just to try each other out, to see how we felt about it. Then I was doing the course two days a week on the side. And the course just exploded. And so I was in a strange position of choosing between carrying on at Vercel or doing this course, and I chose the course, kind of madly, but it's been going great, so I'm really pumped. Paul: It must have been a good opportunity to choose the course because if anybody's listening to us right now, listen to Matt's microphone, I mean, come on, you got the ingredients for a course. Matt has a YouTube channel too, if you're listening and you want to go find out things about TypeScript in particular. That's what we're going to be getting into in this video. We're going to be talking about Zod and new stuff that's happened with Zod. Personally, I'm a huge fan of Zod. I'm nutty for Zod. I use it. I would be crazy not to use it in a TypeScript project to date. But before I've heard of it, somebody was trying to describe Zod to me and I was just like, "Okay, but I use TypeScript. What are you on about?" So why don't we start out with what the heck is Zod, and why would somebody want to use it? Matt Pocock: That's a good question. It's easy to describe Zod as something fundamental to TypeScript, but you can think of Zod as just another library that you install with NPM that you use. And it's basically a schema validator. What that means is it's really good at validating unknown inputs and turning them into outputs that you can then understand and work with and control. So the classic example is a form input, basically, where you have a form and you have, "Okay, I need a first name, a last name and an email." And if one of those, for instance, isn't included, then we need to error. If the email is formatted incorrectly, then we error. And Zod does this. It can do this for forms, it gives out really nice human readable errors, but you can also use it for a bunch of different stuff. Paul: Out of the different stuff, what is that? Because for me, I'm thinking about validation and that's like the extent of Zod. What are the extras? Matt Pocock: Zod, a validation is its bread and butter. And if you think of validation, you start realizing that, okay, from a philosophical point of view, let's imagine that your application that you're building is a system, right? It might be like a front end and backend system, but let's imagine it's just like a backend system for now, like a financial microservice you're building or something. That thing is probably going to have ports and endpoints that are exposed publicly. That means anyone can try and access that and try and use it. You're going to need to make sure that the things that are being passed into that are validated, are in the correct shape, are in the correct format. And so every single endpoint that you have out there should be validated with something like Zod, or another one might be GraphQL, right? Zod doesn't necessarily replace GraphQL, but they sort of do a similar job in that GraphQL... Obviously, GraphQL does a lot more, but it does a lot of this schema validation for you. It says, "Okay, this is a string. This is an int, this is a float, blah, blah, blah, blah, blah." And that's one of the first things that comes to mind for me. Imagine you expose a public webhook from your Discord bot or something. You know that you're going to need to receive messages in a certain format there, in a certain way. So you probably need a schema validator to check that those messages that you're receiving are in the correct format. If you have webhooks bouncing back and forth... Anywhere where you have unknown information entering your system, you probably need something like Zod to validate it. Paul: And one of the beauties about Zod is you can pass in anything, right? We're using TypeScript. So let's take the Discord bot example. You get a type, and you're like, "Oh, I know this message type." You can just pass that type to Zod and generate a schema of some sorts. So we can call it runtime validation. Is that appropriate? Matt Pocock: Mm-hmm. Definitely. Paul: Okay. So when this runtime validation is happening, can we think of it just like a bunch of mental transforms that Zod is fronting for the TypeScript compiler when we're developing? Matt Pocock: Let's let's imagine it from the type level first because the thing that's really good about Zod is it's got a really nice developer experience, it feels really nice to use. And the reason it is really nice to use is that you can build object types out of it. So it has this convention where you say zed dot: z.object, z.number, z.string. I saw someone on Twitter saying, "It's kind of like a German saying it. 'Ze string, ze object, ze number, ze enum.'" And so you use these to construct these big object types, kind of like you would work with interfaces in TypeScript. So we've got this idea that you can create types out of Zod, because Zod also has this lovely thing where you can infer the type from the runtime construct using z.infer. So what this means is you can basically pass anything into Zod and it will turn it into that type at runtime. So you can be really, really loose with what you pass in and really strict with what you get out. That means that Zod fulfills this need that lots of apps have, which is turning things that you're not quite sure what they are into things that you are absolutely dead sure you know what they are. Paul: That's the step when I'm taking in the request.body and I'm doing 1,000 if statements about the different types. Matt Pocock: Yeah, exactly. And it does that in this really nice reusable way. Paul: From my point of view, I mean, it contributes a lot to the developer experience, like you mentioned, when I'm writing with the TypeScript compiler. But if we're thinking about it from an informatic point of view or a runtime point of view, when Zod is out in the wild, does it, in your mind, provide any sort of ancillary benefits besides the work and the attention to detail that is put forth by the team developing it beforehand? Because all this attention to detail is like, "Okay, let's check, let's type check." And Zod's like, "Okay, I got you. Don't worry about it." But in runtime, does it do more for us other than that first- Matt Pocock: Well, at runtime, the thing that's useful about it is it lets you be really, really sure that what you're getting at runtime is what you think you're getting. There are lots of things in TypeScript that tend to return any. You think of any as a bad thing in TypeScript, and it's kind of bad if you just type everything as any, right? You probably shouldn't do that. The thing that is, though, that if you're getting something from JSON.parse, like that method there, that returns any in TypeScript. And so if you're using JSON.parse, you're basically introducing an any into your code base, same as if you use fetch, actually, the fetch API, if you call fetch, res, res.JSON. Res.JSON will return a promise. Inside it has any. So you're introducing these anys. If you grab something from local storage even, you need to make sure that the thing you're grabbing from local storage or the user's clipboard or something, it needs to have a certain shape. And Zod lets you transform those anys into something that can be manipulated and something that you know what it is. The thing you're talking about there with the ancillary benefits is Zod will basically fail at runtime, right? It throws an error. And this means because it's throwing an error, it's not letting this bad thing through into your code base, that you get this really nice experience where the errors happen at the boundaries of your app. They don't happen deep within your app, or they might if you've got some broken go, but that's nothing to do with Zod. They happen at the edge of your app. And when they happen at the edge of your app, they're really easy to diagnose. You know that if you've received some bad information and Zod's thrown this beautiful descriptive error, you know that that bug is pretty easy to fix because you're getting some bad data through. Paul: Right. And they provide even a custom error and you could override that. You could say, "Oh, I have schema A, schema B, schema C, and it's failing in these specific spots." If you're writing an API service, I'm sure you could somehow get exact error message about the field that failed. Matt Pocock: Yeah, exactly. And you can do really clever kind of refinements as well. So imagine that you have a password and a confirmed password. You need to make sure those are the same. That's not something that Zod supports out the box, but you can do z.refine where you can refine the existing object type and check that those two things are the same. And if they're not the same, then you throw an error. Paul: The refine brings me to a second thought here, which is that Zod makes you think about or urges you to think about validation that you wouldn't have considered beforehand. I mean, there are so many methods in this library that you can just chain, because it's a chainable sort of paradigm. I'm a super basic user of Zod. I've used it just for my personal do-dads and this. But from your more bird's-eye point of view, what are some of the things that you think people might miss out on when they're thinking about validation that Zod urged you to think about? Matt Pocock: So I've been using Zod, I use it in a bunch of different places. I've been using it in a really, really interesting case. The case that I've been working on is working on an AST path. What that means at a basic level is, I'm using something called Babel where it will take in a file and it will say, "Okay, it's going to take all of the syntax that's in that file and turn it into this big tree of nodes." At the root is the program. Then you've got all of the methods on all of the individual lines and all the variables and all the stuff inside that. This gets extremely complex to look at because you look at a function and you think, "Okay, that might have an argument. It might have a type argument being parsed to that." The AST is so complicated and so rich and so diverse. So what I've been doing is using Zod to say, "Okay, if it's this thing..." Like, let's say I'm looking at parsing a generic to a type, for instance. Okay, if this generic has type arguments with z.array with minimum length of one with this inside it and this inside it, then using Zod just makes it so much easier to think about that stuff. And using all of those chains and being able to specify, like, "Okay, I want this to be array of minimum length one and max length four, for instance. Otherwise, show a different kind of a..." It's just a dream to work with. It really, really is. Paul: One that you mentioned was the refine. I haven't used that one personally. Just to step into that real quick for my own selfish want to explore that, what does refine do? Why would we want to use it? Matt Pocock: Yeah. Because a lot of what Zod does is it's giving you validation on individual properties and objects, let's say. That's a really common one. So imagine that you want to validate a user type. You want to validate that the email is an email. You want to validate that their date of birth is an actual date. You want to validate that the number of posts they've contributed is a number, all that stuff. It gives you all of these methods to do that. So you have a z.string.email method. But what if you wanted to do something more complex than that where you wanted to combine lots of fields from an object in the validation? So the confirmed password and password one is a typical example. I'm trying to think of another one off the top of my head. Maybe you want to make sure that, I don't know, you have an array of values and you want to make sure that all of them are in sequence, that they're sorted. Then the way you would do that is you would have z.array or z.array with a number inside it. Then on the array you would refine it to make sure that the array where they're all sorted and the array where they're not all sorted is the same. And you can reuse these validations as well. So you would have then a type or a schema which is sorted array that you could then reuse in various places across your application. So refine lets you operate at a higher level than the normal validations let you do. Paul: So could we use this for a password validation? You need to have 20 characters, one cap and one explanation. Matt Pocock: Yeah, totally. Custom errors would work totally fine. I'm trying to think. You could, yeah, with one capital. Async refinements are kind of cool as well. Because with an async refinement, you could call an external API that says, "Is this password okay? If it's not okay, then throw an error." Paul: Wow. You have a whole lifecycle management of input. Matt Pocock: Seriously, yeah. And if we talk about transform as well, which we probably will do later, you can even do async transforms as well. Paul: Let's do it now. Matt Pocock: If you think about z.transform, what that does is... Imagine that you have a string and you want to coerce it to a number. So you want to say, "Okay..." Because let's say you have a text input that you want to coerce into being a number or coerce into being a date or something. Then what you would do is you would say z.string, because that's the input that you're getting from, let's say, a date field, and then you would say z.string.transform. And then you could do any kind of transformation there. So you parse in a function that takes in the input and returns a new output. So let's say it's like you just wrap it in new date from JavaScript. Suddenly you've got a sort of functional paradigm there where you're able to just parse in something and then it's piped to this transformation function. Now, when you think about that, then you think, "Okay, let's imagine that I have an array which can then be parsed in JSON.stringified." You start to realize, "Okay, I can do async transformations as well. So I could just make a transformation where I parse in an ID. It goes and fetches the thing, refines all the data for me." It just gets kind of mad. You start to realize there's this whole paradigm inside Zod that's just hiding inside the schema of our latent library. Paul: It's almost tempting to go down the rabbit hole. I'm like, "What could I do?" Matt Pocock: Because there's even some new methods come out in 3.20 that enable more functional stuff. Paul: Yeah, let's get into that. And before we do, I just want to mention, Matt's been with us before. And if you want to hear more eloquent, awesome ramblings about what's going on in tech, go watch our other episodes and listen to Matt. And I'm sure he'll be on again. Well, let's get right into the new stuff that you just mentioned. Because we're talking about methods. I love the refinement method. I learned something new today. A new one is pipe, right? Is that a new one, to my understanding, in 3.2? Matt Pocock: Yeah, that's new. Yeah, that's 3.20 we're at now. Paul: Not two. Matt Pocock: 3.20. Yeah, I thought it was like 3.2.0. I even did a YouTube video where I said 3.2.0 and then realized it was 3.20 halfway through. So pipe, what that does is you can declare one schema and then you can basically... It's kind of strange thinking about pipe without thinking about functional programming because that's where it makes most sense. In functional programming, you essentially use a pipe operator, just pipe things into other functions, kind of like you would do on the command line. With Zod, you're basically piping the results of some schemas into other schemas. So if you imagine you have a z.string, you would then pipe that into something that accepts a string and returns a date, let's say, and pipe that into something that accepts a date and returns .now. These things are pretty cheap for Zod to add, and it enables a lot of interesting things or interesting paradigms when you start to put it into code. I'm tempted to think of Zod as mostly just like a schema validation library. That's what it is at its core. And then these little extra sprinkles just give you the ability to go that little step further if you need to. Zod is kind of competing against a couple of libraries like Ajv and probably not Joi anymore, but certainly io-ts. io-ts is a really, really functional kind of paradigm and really takes itself very seriously in terms of, "Okay, we are the functional schema validation library. We help you do functional programming." Have you heard of it before? Paul: No, I have not. Matt Pocock: Yeah, it's really hardcore. And the docs as well, they really need you to understand functional programming in order to really use them, and where Zod feels a little bit more user-friendly, but it's got these little extra bits that let you do things that are maybe available in these more complicated libraries. Paul: So the pipe operator, in essence, lets you take a Zod type object and push it into another. And what is the difference of using a pipe versus taking a type of and then pushing that type into a new Zod? Matt Pocock: Yeah, it's just a bit more idiomatic, really. Paul: Gotcha. Matt Pocock: If you imagine that you declare these... Because with z.transform or something, what you would need to do is create a function outside it and then inside that function call like a schema.parse, really, and do that. Whereas, pipe just lets you declare all these things at the top level, like to number, and then declare your transformations outside and then pipe them all together. I don't really see it as something that you're going to be using day-to-day, but it's nice to have it if you're interested in going down that route. Paul: So let's pick one other new method that came out in 3.20. I know there's a lot. Let's see, I have a little list right here. If you have one in particular that you found interesting, please shout it out. All right, here's one I have. What about the finite method? Matt Pocock: The finite? Well, the finite method is super simple. It's basically just saying, "Don't allow infinite into here." Which is to say, because technically infinite is possible in JavaScript to number. Infinite is a number. Now you can say, "Okay, I need a finite number," instead, which is kind of mad, really. Paul: Let's say it was making an N file and parsing the N with Zod. How do I put infinity? Matt Pocock: Yeah, it's possible, though. It is possible. Yeah, yeah, yeah. I don't know whether you can get it from JSON.parse or something. I don't know how you would possibly get that as a result. But yeah, so now you can avoid it. Paul: I have to say, speaking of N, one thing that Zod has been a huge help for on my personal projects is parsing N files. It is amazing to type, "Import N from my utils package and do N.," and boom, they're all there. They're all typed. It's so nice. Matt Pocock: One thing, I mean, there's a really interesting sort of thing there, which is you should definitely use Zod for things that you don't trust for parsing user inputs, for making sure your API endpoints are secure. There's an interesting discussion to be had about whether you should use it for things that you sort of half trust, like yourself, let's say. Because when you're writing a script, you probably know that your environment variables are set up correctly. If the user has done the read me steps or something on your GitHub repo, then in theory they should have all of the environment variables. And there's another one which is imagine that you have a backend team and your backend team are building an API that you, as the front end team, are using and consuming. Should you use Zod to validate what you're getting back from that backend? What do you think? Paul: I feel pretty opinionated on that, which is that if you can give a helpful error message, you better damn be sure getting a helpful error message. I really dislike the attitude in tech where people are saying, "Well, in theory it should be like this. And even though it's slightly more difficult and in the system, in theory it would be better." In the real world, it's never going to be like that. So that opinion becomes a little discounted in my head, because what happens in the real world is what matters in the end. Matt Pocock: It's an interesting question, though, because it's about the level of trust you have on an organizational level almost in that backend team. I can remember jobs I've had where I would have loved to validate everything I was getting from the backend and build from that. And I even use Zod, for instance, in little third-party projects when I'm just using an API that I haven't used before. When I'm using the YouTube API or something, I don't necessarily use their bindings anymore, I just go and fetch it and then Zod.parse the things that I'm getting back. And you even get documentation in the project for all of the things that that thing is going to return. It's just so cool. I think that that's where a lot of the discussion around, "Should I use Zod?" really rests is in that, "How much do I trust the thing that I'm using?" Because obviously you need to use it for unknown stuff, but, "How much do I trust the thing that I'm using? Should I be using Zod to strongly type it?" Paul: I think that makes me ask the question, well, what's the alternative? If the alternative is not using Zod, and then so I'm asking what is the benefit of not using Zod? And it's the time. My first thing is it's the time to save. And besides that, I can't really think of anything else. Matt Pocock: The alternative is to do it all at the type level, is to do it with an interface, let's say, because you're getting any back from your fetch method. So you would just say, "Okay, the result of the thing I'm fetching: YouTube result or something. And that basically takes all of the stuff that you're getting from Zod and just puts it at the type level, which means that, first of all, you don't need to use a whole library. So you don't need to read the documentation. You don't need to have that kind of overhead in your project. And also, it's faster. It's going to be faster not to validate, of course, because validating, especially big objects like that, will always be slower than not doing it. The only way to speed up a program is to do less stuff or do that stuff faster. So yeah, that's obvious. And Zod has a significant bundle size. If bundle size is a concern, and I'd argue that for most apps it isn't or it's not as much as you think it is, and not as much as Twitter thinks it is, if bundle size is a concern, then you might not want Zod on your front end validating everything that is pulled through, and you might just want to do that on the type platform. That's totally fine. So I think there is a conversation about whether you should use it. And certainly, teams out there will be having this conversation. I think it's mostly about, do you trust the stuff that you're getting? If you don't, use Zod. If you sort of do, it's definitely a conversation. Paul: And on the bundle size comment quickly, I think the opinion of, "Do we really need to worry about bundle size?" "Not really," is a very needed to be said thing because we worry a lot about crossing Ts and dotting Is, but we're at a point in hardware where very few things matter for a software developer's point of view. We're at that point. I mean, if we look at Cloudflare Workers, they're considered to be a pretty restrictive runtime environment. You can have, I think it's a megabyte max for your bundle. And I've used Zod several times in many Workers, and this is on large enterprise projects. And I don't know, it's fine. We still have plenty of room to add more modules. So the bundle size argument is a tough one to get by me. Matt Pocock: Yeah, it [inaudible 00:25:27] vastly. I've had this argument a bunch of times, actually, because I used to be on the core team of XState. And XState is kind of similar to Zod, which is XState is a library for building finite state machines and state charts and it brings a lot of robustness at the cost of being a little bit hefty to add into your bundle, 40, 50 kilobytes or something unzipped. It gives you a lot of safety because state charts and finite state machines inherently are very safe. They don't allow crap to come into the system. This would be crap sequences, impossible states, impossible events. Certainly, just like Zod, Zod doesn't allow impossible data into your application. So both of them have the same sort of cost benefit analysis. And I would argue that in most sectors, or probably half, let's say, sectors out there, robustness is more important than speed. And in the other half speed is more important than robustness, like if you're doing an e-commerce platform or things like this where you just need to save every single millisecond. But in my experience building stuff, robustness has always been super important. So I always super highly value Zod and XState as well. And that was the thing I came on to talk about last time actually was XState. Paul: Well, we're kind of coming up on time, so this is a good segue. If you want to listen to more Matt besides our podcast, you can go check out his YouTube channel. I'm a fan of Matt's YouTube channel. It's a good YouTube channel. He has videos about TypeScript basics, fundamentals, just developer ramblings. Besides a YouTube channel, Matt, where could people find you if they want to listen or read? Matt Pocock: So the home for everything me right now is TotalTypeScript.com. That's the course I've been working on. It's the course I quit Vercel to make. And it's basically everything I've learned about TypeScript from working on XState core team from leading TypeScript teams, from getting deep into all of the TypeScript wizardry out there. TypeScript is going to be probably the number one language out there. It's already beating JavaScript in terms of adoption and in terms of amount of scale at industry level. If you want a job right now, you're probably going to need TypeScript. I've got a lot of beginner material up there that's for free. And the advanced material, there's a lot of advanced material up there for free as well, like in the tip section. But I've been building an advanced TypeScript course. So the beginner material should get you the job and the advanced material should turn you into a mid, into a senior, into a lead and teach you all about the kind of magic that lives inside some of these libraries like Zod. Because Zod has an extremely dense and complicated code base to look at if you don't understand type level programming, if you don't understand generics. And my workshops and the course that I'm building is all about that, all about taking you from a mid-level, intermediate TypeScript person into being full TypeScript wizard. Paul: One more time, what was the name of that website? Matt Pocock: TotalTypeScript.com. Paul: And that's the name of the course too, Total TypeScript? Matt Pocock: Total TypeScript. Yeah, that's it. Paul: All right. Well, Matt, thank you for your time again, and hopefully some people can get their attention turned onto Zod if they're not already using it. Matt Pocock: For sure. Really nice talking to you.