Check out a free preview of the full Advanced Redux with Redux Toolkit course

The "Partial Types for Incomplete Data" Lesson is part of the full, Advanced Redux with Redux Toolkit course featured in this preview video. Here's what you'd learn in this lesson:

Steve uses Partial Types to eliminate TypeScript errors for missing properties and keep business logic inside the reducer and away from the view layer. Partial Types make all properties optional. A utility function is used to hydrate the missing properties on an object before it's pushed into the state.


Transcript from the "Partial Types for Incomplete Data" Lesson

>> Let's talk about just my strategy, right, for dealing with this. We will talk to APIs later and stuff along those lines. But one of the questions that came up the other days during another workshop was, when you don't immediately have IDs for stuff, what do you do?

And this is true. If you're dealing with stuff client side as well, you need to give it unique identifiers normally for keys. If you're talking to an API, hopefully your server is doing that as well. As long as you're getting from somewhere, it doesn't really matter. We will eventually talk to APIs, but for now we'll kinda see what it looks like here.

The error, just to be clear, we're saying it's a task where we're obviously missing the ID, right? Fine, could you put it in here? Could you come up with an ID? Could you use a library in here? You absolutely could. I would argue that you should not, and this is mostly a kind of strategic thing and a belief system that this is your view layer, its job is to know what the current state is.

And to deal with user input like stuff getting your model ready by assigning an ID and formatting it, should not happen in your component, right? I believe that all of the kind of things that create and set up all of your state should happen inside the reducer, right?

And so we wanna give ourselves the primitives to make that happen. And that way, it's all in one place, you don't play where are all the places that I get this thing throughout my different UI? Having it centralized, it is very tempting to do it here. I have been tempted.

I've done it here before, I have regretted it. So I'm not doing that while I'm being recorded. We're gonna do it right. So here, we have a bunch of ways, and this involves just a little bit TypeScript knowledge that can help us, right? Could you just define a version of the type that has different optional properties?

You absolutely could. My kind of move with TypeScript in this case is trying to assign stuff and say this thing is another thing. A lot of times doesn't work super well for me, there's nothing wrong with it. You can totally do it. You can say, you can cast as a type.

I like to make functions that will say, take a partial version of this and fill in all the actual details to make this a real version that something might be expecting, right? And one of the ways that you can do this is there's a utility method in TypeScript that's a partial that lets you kinda just pick off certain things.

So what I will do a lot of times, and I usually will not export this type until the need arises. If I need it in more than one place and fine, but a lot of times, I want this to kind of be an internal implementation detail in my either slice or reducer or what have you.

And I'll call it DraftTask or something like that, whatever the version that does not have all the things that a real task needs. At this point, the simplest version that you could do is just say it's a partial of task, right? And if you've never seen this before in TypeScript, this is simply saying, take all the required properties and make them all optional, right?

Gotta deal with why users any in a second. But for the most part, cool. Now, it is one way I can actually have a draft version. Now, that's still not going on my array. I've just moved the problem from getting yelled at when I change it here, from getting yelled at when I dispatch the action, which again, I love that I was immediately yelled out for that.

Now, I've moved it down one line. And I do have an old saying, which is as long as the error is moving, you're making progress, right? So I will take that because now it's like yeah, cool. The action can be a draft task, but you still can't push one of those on there.

So the pattern that I really like for this is I will a lot of times just have a method that will take the thing that I have and turn it into the thing that I want. And appease TypeScript rather than casting it and doing all this kind of stuff, I'll basically say something like createTask, right?

And that will take a DraftTask. This is one of those things where I define the return value up front, not because I know that TypeScript will automatically assign the right type. I do this even cuz it doesn't necessarily have to be called a task as long as it meets the requirements of a task.

I do this to make sure that the code I write is fine, right? It's a fun little unit test right for me right here right now by defining the return type as I start to write the function. So it's also strategy that I really like in this case.

And this case, really, I know that the thing that I'm missing here is that I need an ID. Lots of ways to do this, Nano ID is around. Nano ID is also included in Redux toolkit, so you don't have to get another MPM and import, or install rather, right?

Personally, it's like, yeah, I'm excited to start a new thing. And then it's like death by MPM installs as I remember everything that I need in a modern web app over the course of the next several hours. I also have it in the package JSON too. So we can do that.

We need to basically say return, and we can say, everything that's on the DraftTask, but then also make sure that we've got an ID, that is you can see I've got to them. We'll just pull the one from Redux toolkit. Let's see.
>> You mentioned user ID the other day in React, is that?

>> So user ID is different. I think I have that somewhere, maybe not in this app. Let's talk about user IDs for a moment cuz it's a great question. User ID is a hook, so it's only available to you in React components. User ID does give you a unique value.

It is predominantly for giving you a unique identifier that is both consistent on server rendered components in React as well as when they're hydrated on the client. For instance, for a form, you should have an ID on the inputs, and then the label has an HTML4, right? But sometimes, especially if you're generating on the server or you have a bunch of these things, you might not necessarily at the time have a completely unique identifier for that.

So you can use user ID to create an identifier for yourself, cool. And so let's see. I think I did this the other option, the other order. Because since ID could be undefined in my partial task. See return, what are we getting yelled at for here? ID, we've got string, we got to actually say that these exist.

So really, actually, what the DraftTask should be, we know I got halfway there partial. The other thing we can do is the partial is everything's optional, which I can combine this one. But really, I do actually want the type safety to make sure that I have a title.

It's like, hey, cool, everything's optional. You set an ID, titles can't be optional in real tasks. Everything else can be, or you've set the ID, the user and the column can be. So partial make everything optional, and I'll build towards this in a second. Really, what I also need is I just need to pick certain things out too, right?

And so I could say that we do want to pick the title off of there as well. And I'll show you why like where I use both of them together in a second. So we say, pick, we say the title. So now, this is expecting what we've only got the title, right?

You can kind of see. It is sometimes nice if you have some of the other information for whatever reason. You could imagine that maybe when this became a full comp on board, I already have the column, or if it was from a user page, I might already have the user ready to go as well.

And so I'll show you a helper type that I make in a second, but let's kind of get the rest of this wired up, and then I will show you that. So we've got that, and now you can see this counts as a real task as soon as I call it.

So I can say, createTask, pass it in that action payload. And boom, the types are happy, thereby I am happy.

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