Intermediate React, v5

Typing Component with a Form

Brian Holt

Brian Holt

SQLite Cloud
Intermediate React, v5

Check out a free preview of the full Intermediate React, v5 course

The "Typing Component with a Form" Lesson is part of the full, Intermediate React, v5 course featured in this preview video. Here's what you'd learn in this lesson:

Brian completes the refactoring of the application to TypeScript by adding types to the form data in the SearchParams component. The Details and App components are also refactored.


Transcript from the "Typing Component with a Form" Lesson

>> Okay, useContext, useState, useEffect. Okay, we have this, I mean, we can identify this, it's an animal array, right, which is kind of fun. So animal response types, it's an array. And now what's cool about this is anything that we know that we pull out of here is definitely gonna be a valid animal.

And again, I can't put horse in here now, cuz it'll be like, nah, it's not a valid animal, right? TypeScript knows, it knows that it's animals, it paid attention when you were showing it videos. SearchParams, that's fine. UseState here. This one here, right now it thinks it's just a string, we actually wanna identify this empty string is an animal.

And now it knows that it's definitely gonna be an animal, which is useful. Okay, requestParams here. That's interesting, we can get to that here in just a second. Pets is gonna be returned as pets any. Well, that's not what we want, we assume that we want this to be search.

Okay, results, that's fine. So what are you mad about here in search? We've identified this is gonna be a PetAPIResponse and a location, animal and it's breed. So that's a query function, okay? Going back to searchParams here, we have fetchSearch, it knows that that's gonna be search. So what do we have here in useQuery that it's mad about that?

All right, well, let's keep going, and I'll look back at this here in just a second. On the form submit,, we just need to identify that that's definitely gonna be, okay, yeah, no, this is fine. Instead of having, put e.currentTarget. I know this is a little strange, but we're not actually guaranteed on these React form events that target's gonna be defined, despite the fact that it always is.

But currentTarget is guaranteed based on the spec. You'll find that, again, with TypeScript, you end up doing things like this. You'll learn the spec very well, because you're gonna have to have guarantees for all of your code. So target and currentTarget in this particular sense are always gonna be the same thing, so that's fine.

And then the last thing here, you'll find that formdata.get actually technically returns a form data entry value, which is not technically a string, which is annoying. So we can do this conditional ?.toString, and now we can be guaranteed that animal is going to be a string. So just put that on all three of these.

Why was it working before? Because implicitly, it would call toString for us. And so now, when we call setRequest object, it's getting back string, string, string, which is exactly what it wanted. And then on, again, here I think this needs to be target.value as Animal, and as Animal.

And then pets down here, it's still mad about this, which I'm confused about. So string; animal: string; I think this you have to say this is as Animal. There we go, okay, took me a second, so this is what went wrong in my notes. I made the fun audible to call that fetchSearch here.

This is an Animal, in my notes, I just have this as a string, right? Technically it's an Animal, you should type it as such here. Going back to searchParams, so is like, hey, you've given me here a string, that is not an Animal, right? So here you have to identify that this empty string is an Animal, and therefore, it'll be happy about that.

And same thing here, if you're gonna do that, you have to say this is as Animal. As well as this is as Animal. As Animal. Okay, so if you're following along, and you wanna have that typed as an Animal, I probably would, because it's supposed to be one.

You just have to make sure that you're casting it on all the correct places to be an Animal type. So, I mean, that's probably useful for you to see how I would debug that, right? If we look at this, it was saying that this is improperly being overloaded.

And then so I was looking through it, it was like, okay, this type here is not castable to this. And I saw that animal here was being typed as Animal. And that's when my light bulb went off of like, okay, We have to cast this as an Animal.

All right, any questions about that? TypeScript, stuff like this catches me all the time.
>> Would we also wanna cast breed, since we have, finally, those things that are breeds? There's just a lot more.
>> I thought about doing that, but yeah, there's a lot of breed types, right?

So if you really wanted to, and the other thing is we're not adding too many animals day by day. But I could imagine that our API could be adding and subtracting breeds all the time. So getting a complete breed list to be in enumerated type, one, I don't think it'd be particularly useful.

And two, it'd be hard. So in that case, I would say the juice isn't worth the squeeze, whereas working with animals directly, there's only so many of these, right? We're not adding new species of pets every day. I'm not, maybe you are. [LAUGH] But yeah, cuz we don't have to have this animal type, we could totally just say this string type and move on.

But this is such a limited set that it made sense to me. So that's kind of up to you. I would not do breed, I think that would be a bad idea, but it might be, typing animal might not be worth it. I guess, it's probably what I would say, cuz you end up doing stuff like this.

Okay, questions, results is the only thing here. Yeah, so I think we still have to go type results, but otherwise we're pretty close there. Results, Results.tsx. We do have kind of an awkward thing here, remember I called my API response type here Pet? This is why people like to call it IPet.

But it’s not that big a deal, all I can say is import { pet as petType } from './APIResponseTypes'. And now I have the Pet component, and I have the Pet type there. I don't find that particularly confusing, but other people would take great exception that. So pets here is going to be an array of pet types, so we're just gonna say pets : petType array.

And that is enough to type all of results. Yeah. Okay, and let's go do App.jsx. So app, make that App.tsx. UseState here, we have to say that this is either gonna to be null as Pet or null. We need to import that Pet type as well. Because otherwise, it assumes that this useState is always gonna be a null, right?

So we have to say that this could be a null or it could be a Pet, that's fine. And here, just before this, we just have to say if there's no container, then crash the whole app, because we give up. throw new Error ( "no container to render to").

Which it would do that anyway, but we're just gonna be really extra explicit about it. AdoptedPet or null, dispatch is not assignable to Pet. Okay, well, that's fine. So we do have to go back, it looks like to, Our adoptedPet here, and we have to say that this can be a Pet, or it can be null.

Okay, and that'll fix the happiness here. Cuz in theory, it will be null most of the time, right? If someone hasn't clicked Adopt a Pet yet, that's gonna be null, so that's actually a valid thing for it to complain about. Okay, we still have this Details one to suss out of what is upset about.

So what I had here before in Details, I had this PetAPIResponse, it actually already knows that, right? So, interacting with this generic this way was not the useful thing to do. So, in order to fix this, all we have to do here is delete this. And that is correct.

Looks like there's a warning in here, which we don't need this PetAPIResponse anymore. Okay, there we go, we are now up to TypeScript-4, and as you can see now, we have an entirely typed project, there are no any's in this anymore. If we run npm run lint again, we shouldn't have any errors here, we don't, we can do npx tsc --noEmit.

This will run the TypeScript checker, everything's working like that. In your package.json, go ahead and add the typecheck here, and you just put tsc --noEmit. So now I can do npm run typecheck. And there you go. So again, one of the reasons I add all of these. So now when I have my GitHub action that I set up later, I can run typecheck, I can run lint, you can run format check if you want to.

So that with format, you can put like a format:check and it's instead of right, you do, what is it? npx prettier -- help. It's like list different, or check, they give you a new one. So then you can just do that --check instead. So then you can do npm run format:check.

So this is like if you have someone that's like working in your project that's not running prettier, and you're getting sick of them committing files to your project that are not run through prettier. You can have CI basically say, if you don't conform to prettier, then I'm gonna fail your build, right?

And then yeah, you run typecheck, you run lint, you run format check, and then we're about to do unit tests, then you run all of your unit tests as well. And you can be pretty assured that all of your code is formatted and to style and working before you send it out to production.

>> You pretty assured?
>> Pretty assured, it's gonna be pretty good.
>> You're sure?
>> [LAUGH] Yep, so again, this is just kind of a preview of what TypeScript in React is like. If you wanna go get further into depth with it, go check out Steve's course. If you want more TypeScript goodness, go check out Mike's course, Mike North.

Learn Straight from the Experts Who Shape the Modern Web

  • In-depth Courses
  • Industry Leading Experts
  • Learning Paths
  • Live Interactive Workshops
Get Unlimited Access Now