Introduce a Type Parameter to Ensure Type Consistency
The first thing we need to add is a type argument for
We'll start by scaffolding a type called
MakeInfiniteScrollParams that will be an object containing
For now we'll set
initialRows to be an array of
fetchRows to be a function that returns a
0:00 The first thing that comes to mind is we're obviously going to need a type argument for makeInfiniteScroll. The question is what we should be expecting in that type. For now, let me just scaffold a couple of things. I'm going to say, "interface MakeInfiniteScrollParams."
0:17 I like extracting this out to an interface or a type. Let's go for a type, actually. MakeInfiniteScrollParams and just scaffold this here. We know that we have initialRows, which is probably going to be an array of something, and we have fetchRows. It looks like fetchRows is going to be a function. It doesn't actually take in any arguments, which is good. Let's say it just returns unknown.
0:44 In fact, I think it might be best if it returns a Promise unknown. We know that, basically, it's going to fetch something. We need to actually pass in a Promise here. I guess maybe we could do a maybe Promise. Anyway, we'll get to that.
0:59 We've got this unknown here. We can see that data.push...Type unknown must have a Symbol.iterator method that returns an iterator. What? It turns out the data.push expects it to be an array of something. Because we're spreading this in, we know that this is probably going to be an array of something, which makes sense because we're fetching more rows here.
1:25 We've now got this key here. Key doesn't exist on the params. Let's add that in just as a string, for now. Let's see if things have improved at all. Oh no. No, they really haven't. Why isn't this erroring? Oh yeah. Because we don't need to pass in the initial rows. This one can actually just be an optional here.
1:48 That's good. We've removed some errors, but this table, for instance, it's now just...fetchRows is returning Promise unknown. That's good. table here, getRows is returning an unknown array, which is going to be complaining down here. It's supposed to be an array of the things that you pass into initialRows.
2:10 Now we can see that we have a couple of unknowns here. We have this key string, which I'm just going to type as unknown as well. Now that we have all this stuff, we can start to see relationships between all the pieces. We can see that this type probably needs to have a T on it, like TRow, for instance, that represents all of the pieces here.
2:31 initialRows, it makes sense for that to be an array of TRow. fetchRows is also going to be an array of TRow. We can represent this pretty safely in an object here because we know we're going to be dealing in arrays. We know that TRow array is going to be there, and Promise TRow is going to be there.
2:50 Now we need to find some way of passing it into the MakeInfiniteScrollParams. We've got TRow here. From all of our pain that we've been through, we know that we can add TRow here, and it's going to be in InfiniteScroll there.
3:07 How are we doing now? We're getting pretty nice functionality here. We're getting getRows. We're actually passing this test down at the bottom. Our rows are now inferred properly based on name and id. If we add age, 23 there, let's say, then this is going to be added to this too, which is fantastic. This function is now inferring properly. Brilliant.
3:29 What's erroring at us now? We have a "Should ensure that key is one of the properties of the row." We know how to do this. We know that TRow, we can use keyof TRow to make sure that this key is one of those members.
3:45 Now, if we go down to the bottom here, we've got key id. We'll get id and name here. If we pass in age, it's going to yell at us. If we pass in name, it's going to work. Pass in id. It's going to work. That is fabulous. This is erroring properly because we don't have name on this particular object that we're inferring from.
4:04 What can we take from this? We know that we can pass in...There's multiple inference sites here, so the initialRows and the Promise TRow. This is a really, really neat one, the fact that you can infer from a Promise that's passed in.
4:19 I'm just wondering, if we don't pass in a Promise here...We have fetchRows. Let's just say that we return this, for instance. This is actually going to yell at us because it's expecting an async function here. This doesn't seem, to me, to make sense. We could say this returns a Promise TRow or TRow array here.
4:41 Now this will work because it doesn't need to return a Promise. It can return a Promise if it wants to, but it doesn't need to. This will work as an async or a sync function. There you go. A little bonus for you. This is pretty nice. I'm pretty happy with how this API turned out. Well done, if you found the solution.