
Lesson Description
The "Type Guards vs. Schema Validation" Lesson is part of the full, Fullstack TypeScript, v2 (feat. Zod) course featured in this preview video. Here's what you'd learn in this lesson:
Steve compares type guards with schema validation libraries. Type guards often require extra conditions to ensure a value has the correct shape and is the expected type. Scheme validation libraries provide a more readable and concise API for this same validation.
Transcript from the "Type Guards vs. Schema Validation" Lesson
[00:00:00]
>> Steve Kinney: So let's start to, like, explore a little bit on this idea of how can we make sure things are the way that they are at runtime? And one of the strategies for doing that is something called a type guard, right? And this is just a native TypeScript thing that you can do.
[00:00:19]
I have done and the problem is that it works, especially if you decide that something's going to be unknown rather than any, because the goal is to try to get all the any's out. Any any is an insist like it will go and just pollute literally everything. So sometimes it'll be like, okay, I'm gonna call it unknown.
[00:00:38]
Well, then you have to ask literally everything about it. It doesn't know what type it is, doesn't know anything about it. And even in the simple use case that I can fit on a slide, it gets ridiculous. So let's say, hey, I got an individual task from my todo list.
[00:00:58]
Because, spoiler alert, it's gonna be a to-do list, right? Because did I try to come up with something that wasn't a to-do list? Everything is really a to-do list at the end of the day, right? All apps, everything you work on that's super sophisticated, is really just a to-do list at the end of the day, maybe a set of to-do lists, so on and so forth.
[00:01:15]
But there's a reason why it is the example that we use for everything all the time. So a given task. Well, we need to make sure, we gotta figure out that it's an actual task. And JavaScript, the wonderful programming language that I use every day is not doing us any favors here, right?
[00:01:33]
We could say something it should be an object, right? Let's try to round it down. Should be an object. If it's not an object. Then it's not a task, right? But technically null. If you do type of null, guess what null is. It's an object, right? So you also have to make sure it's not you have to make sure it's an object, and you also have to make sure it's not null.
[00:01:55]
You'd be like, could I just like, make sure it was like, not falsely in the beginning, yet, you can do that too. And then for every property. You've got to make sure that is what you think it is, right? And if it's not, then you don't have the thing you think you do.
[00:02:08]
And again, it is we are lucky on the day that we get an object back from an API, and it's not like a deeply nested object, right? And you can, I've got some that it's going, get one of some of them are very primitive values, like a number, string of Boolean.
[00:02:26]
Great love to see it right sometimes, then there's another object, and that object has another object. And I've written some of these in my past, version of my life that are increasingly Fraught with peril, right? But there is still some other problems here, which is we kind of look at the return value of the function up on the first line towards the end, you are going to say, if it passes all of these conditionals.
[00:02:55]
Then I guess it must be a task, or my logic was flawed, one of the two, right? If I made a boo boo. Typescript is gonna think it's a task no matter what, because I said it's a task. And Typescript's like, II Captain, here for it, let's do it, right?
[00:03:10]
If I messed up in some way, shape, or form, and like, I say this 'cause I have messed up in some shape or form, right? In various points of my life, then any confidence that I think this gives me is not really worth anything, right? And so in this case, right?
[00:03:28]
Like, especially if it's a nested object, because, like, even at the bottom, though, you can say, I'm not incredibly confident here, right? If something gets added by the API, I might have a new thing. If I have some way, like, who knows the ways I can mess this up or even, like.
[00:03:42]
Again, there are little things of like, is that Id really a number, or is it like, one in quotes, two in quotes so on and so forth. And getting a little more nuanced about this, and like, this is already hurting my feelings, and that's the one I can fit on a slide, right?
[00:04:04]
I have written much worse than this. So luckily, I have a heuristic, which is if something is terrible and feels bad, probably you're not the only one that feels that way about it, right? And if that's true. Then there is a library or an opportunity to make a library.
[00:04:28]
Let's just hope there is a library to deal with it cause a lot of times like, I'm gonna make an open source library is the last thing your manager wants to hear. I say that as a manager. So there are some strategies around, there's a bunch of different libraries.
[00:04:40]
We're gonna look at one of them and I'll peek at some of the other ones in a second. We're gonna look at one of them today, called Zod. Zod is a library that is built to, kind of give you an abstraction for making sure an object actually validates as the object that you think it is.
[00:04:59]
And there are some other ones, like there's another strategy using this thing called.JSON schema that we'll take a look at in a second. JSON schema is just some rules about JSON to describe what a JSON object should look like. Again, these are just schemas for saying, here is a serializable way to say like, hey, I'm expecting this to have an ID that's a number, a title that's a string.
[00:05:22]
Description that's an optional string, a completed that's a Boolean, a way to kind of like, have a standard way to do that. Zod can like be translated into that. JSON schema can be translated back into Zod. The reason I picked Zod for this course is it's also like for a lot of the structured I.
[00:05:42]
Outputs from a lot of AI models like the OpenAI, one will take a dot schema out of the box translate to a JSON schema for reels and be able to kind of give you that structure back as well. So it is, I think, at this moment probably one of the more popular options some other ones.
[00:05:56]
You may have heard of that will kind of we've got some slides for which is like IoTs and yup. Yup wins for the best name. And when I show you them on slides later, you will see that if you get really good with the learning curve for Yup, if it's asking your code base, it's like they're basically the same.
[00:06:18]
In fact, a lot of the creators of these libraries are actually collaborating on a standardized schema that can kind of be shared across all of them. So what, not only are they somewhat the same now, they aspire to be a little bit more of the same than we saw then they are even right now.
[00:06:36]
So we saw that a wild get simplified, honestly, is a task function. What would that look like as a Zod schema? And this is you can add a little more detail to this, but this is the simple version, which is to say, hey, a task ought to be an object where the ID is a number, the title is a string.
[00:07:00]
I didn't put the description this one because completed as a Boolean. And you can also do some interesting, there's some nice like abstractions around this where you can say like, hey, ID should be a number or something that I can coerce into a number, right? So, if it is like, yeah, again, the number one, two, three in quotes, right?.
[00:07:20]
And you could parse in that into a number very easily. Do that and that also, when you've done parsing it, give me the number, right? Same with the booleans. If it's like, sure, true again, you opt into that. It's not gonna do it for you under the hood, unless you choose to do it.
[00:07:35]
But you can say stuff like, hey, this should be a Boolean, or something that coerces into a Boolean, like 01, should be fair game in this case, right? And so a lot of that other stuff that you might write a bunch of code to do yourself, you are now off the hook, and it will be handled for you.
[00:07:51]
We'll see a lot of that. You can put custom error messages so that if it does blow up, you can kind of see, you can actually even display something nice on the page, right? Because, like, you can handle all that stuff, so on and so forth. And, yeah, there's, you know, a lot of the things you can do in TypeScript are also available and usable.
[00:08:12]
You can do unions, you can pick, you can omit, you can do partials, so on and so forth. And the other nice thing is, with literally no extra code if you want, there is a utility type. The type that comes with Zod, that will take a schema and actually give you back the type, right?
[00:08:29]
So you could theoretically, instead of Leo, when I did that, is task thing. I normally had the type, and I was trying to reverse engineer it, right? And sometimes you want to be able to use those values, and it's really hard to like let's say I had a bunch of statuses like pending, done, running so on and so forth, right?
[00:08:48]
I both wanted to use those in my code and also have them be the type. So then I would like do weird things with, like an array as const, and then try to do a type of so on and so forth. Because sometimes you both need them as runtime values to, like, put in a select box for the filters, as well as types to make sure things are the way you want them to be.
[00:09:05]
So this makes it really easy as well. And don't worry, we will play around with this a lot. So once you have the schema, you can go ahead and you can grab anything that comes from your API and you can go ahead and parse it, right? If it passes, guess what?
[00:09:23]
You know that it is the type. In fact, this part's method will take that any or that unknown or what have you. And it will actually, now it will be that type going forward, right? And so now, as long as you do this at those scary intersections between the back end, the Front end, the back end and the database.
[00:09:47]
So on, or honestly form inputs or anything, you get users is the wars. We're making fun of backend engineers. We all know who the real problem is, so users, right? And so we can validate input from users, so on and so forth, right? So we can call parse and the only kind of like nuance for parse is that what does it do?
[00:10:09]
If it doesn't parse it? It throws an error right? Which, I think, actually like we're all scared of throwing and catching errors a lot, right? But it's actually pretty powerful to actually If it is not the thing that we think it is, maybe we just blow up and try to instead of silently hiding it, like we do in a lot of cases.
[00:10:31]
But being able to say, this is not what I think it is, stop everything. And let's go to the catch part of the try catch block is, I think, a lot of times, an underused pattern as well. But if you don't need that, or you don't want that, or something along those lines, that is problematic and throwing is not an acceptable use case at the moment, there is also a method called safe parse.
[00:10:51]
Safe parse doesn't throw, right? It will return an object that has a property called success, which is either true or false. It will also, if it is not the thing you think it is. It will also have a property that will tell you what is not the thing you know.
[00:11:12]
That was, we were expecting ID to be a number, but it turned out to be a string. The same things you would see from TypeScript Zod will also give you, and you can kind of handle them at run time. So maybe it is you want to do something special with it.
[00:11:23]
Sometimes The API is wildly different than I thought it was. Throw up an error page. Sometimes it's like, we can navigate this, right? It's broken in a way that I can handle, maybe we just don't show part of the information, so on and so forth. So either because you'll get the error with the regular parse when you throw it and catch it, or here you'll get the errors in the object.
[00:11:47]
And if you there are things you want to do, which is just kind of like, hey, I can use some of this data, but maybe I can't show everything. You can kind of navigate that, but that's kind of a individual journey as well. In this case, like you won't get the typing of it.
[00:12:05]
But you can do that kind of type guard typecasting that we saw from the previous example. But the important tasting note here is a lot less lines of code in that first one I wrote, right? And because I am literally parsing against the schema, right, I do trust that I didn't make a boo boo as much, especially if that task type was generated from the schema.
[00:12:24]
If I did that, infer that we saw in the previous slide here, right if task is coming from the schema and safe parse says it matches the schema. I am a lot more confident in that case, that everything is what I think it is, that it is, if I wrote the code, maybe as the end of the workday, which is when I do the most technical debt.
[00:12:44]
And again, day, it's never the morning. It's four o'clock in the afternoon is when my worst choices emerge. Like I said before, there are some alternatives. I will show them to you now so that you can see that they're all the same. So this is yup. As you can see, you define.
[00:13:04]
Find an object or a schema that has an object, and then you say that it's strings and they're required. These are all methods you can do on Zod as well effectively and it's a.validate instead of.parse great. Iots, again, same basic idea here. They have that data that's unknown, it's decode instead of parse.
[00:13:31]
It's like they're roughly the same. So if you're like, okay, this is cool, but I use one of these other ones in my in the app I work on a day-to-day basis, the conceptual parts still apply here.
Learn Straight from the Experts Who Shape the Modern Web
- In-depth Courses
- Industry Leading Experts
- Learning Paths
- Live Interactive Workshops