This course has been updated! We now recommend you take the TypeScript Fundamentals, v3 course.
Transcript from the "Challenge 5: Solution" Lesson
>> Mike North: We are going to go through one of many possible implementations of a typed stack data structure, where we have a type constraint in terms of what we can push into this stack. What we have in the screen now is the interface that the stack must implement, that's how we would describe this, right?
[00:00:21] Whatever we make must conform to this shape. So just to recap, we have to be able to push one item. And we get the stack back out so that we can chain these things together. We can push many items at once, using also a function called push. So we have some overloading going on here, right, two function signatures.
[00:00:41] We can pop an item out, and we have to be prepared for the situation where we may get undefined, for example, if there is nothing in the stack, right? We might get undefined out. We began today talking about how types let us express our intent better, and here it's very clear that whoever receives this must be prepared to potentially receive nothing at all, right, receive an undefined.
[00:01:10] And then we need functions that return length and print the contents of the stack out. I did not define what print had to do, but it's for your debugging pleasure, you could have taken that any way you wish. And now we're gonna do npm test stack.
>> Mike North: All right, seven failing tests.
[00:01:37] So the stack's available, new stack should have a size of zero, pushing an item should increase its size by one. Pushing a few items in to the stack one by one increases its size appropriately. Pushing a few items into the stack at once increases its size appropriately. Apparently, I didn't rename one of my tests here, this is one of the things I guess I forgot to double-check.
>> Mike North: Yep, actually, it is a similar test, it's just in, nope, it is not a similar test. One by one, at once, that is exactly the same assertion.
>> Mike North: Guess we don't need that.
>> Mike North: Okay, and then finally the last items pushed on are the first to pop off.
[00:02:28] Last on, first off, right? So we're pulling stuff off of the top of the stack, and then pop returns undefined if the list is empty. So that is our target, let's aim for it.
>> Mike North: So here's the stack, and the first thing we do is we'll say this class implements the interface.
[00:02:53] Oops, sorry, implements IStack. And we're gonna say the Stack receives a template parameter T, and implements the IStack interface passing the same template parameter along. The complaint we're getting at now is it saying, well, now go ahead, implement this stuff. So I'll just paste that down here. And now we've kind of like, we've made this interface part of the equation happy.
[00:03:22] It's saying, well, okay, we have some stuff, but now the implementation is missing, and it's not immediately following the declaration. And by the way, in a situation where you have, remember we had like red lines all over the place when we were sort of midway through some of these solutions?
[00:03:40] This is a nice way to sort of get a small error, to not bombard yourself with indications as you flush this kind of thing out. This is sort if a more subdued situation we got going on here. So I'm going to implement this as a linked list based stack, just for more excuses to use types.
>> Mike North: And we'll call this an interface that's a StackNode. I could create a new class, I don't really need to because this is really just about data. And I can just look with object literals, I don't really get benefits of a class other than being able to refer to it by name.
[00:04:28] So we'll say that this is gonna have a property data called T, and it's gonna have basically a pointer or a reference to another node of the same name. So this is basically like it has the thing of interest, the value of interest, and an edge to the next item in the linked list.
[00:04:55] So we can kinda chain these together. So within the stack I can have my data stored as so, private IStackNode, or sorry, private,
>> Mike North: Head of type IStackNode.
>> Mike North: And we'll just leave it undefined there, right? So here, only within this function, can we access this. The world of prepending underscores before variable names to indicate privateness is a thing of the past, we don't have to do that TypeScript.
[00:05:36] This is actually private, not just improper to touch. I guess, is that the right way to describe the way we treat underscores? So now, this puts us in an okay state here where we can start working with this stuff. So let's do print first because I feel that'll be something useful as we start flushing out this class, we'll need to see what this looks like.
[00:06:10] So what I'm gonna do is I'll say, we'll grab the head node, which we can do because it's private. And then while the node is still truthy,
>> Mike North: Log out the data,
>> Mike North: And traverse to the next node. So we're gonna walk along from the head node all the way until we get basically an undefined for that next value, which means we're at the tail of the link list.
>> Mike North: We'll just change all these to functions, just to quiet some more complaints there. Okay, so now print works. So length, length is gonna actually operate in a very similar way.
>> Mike North: That's fine, right? So we're gonna walk along the list and count our steps.
>> Mike North: So you don't get rid of that, collapse that down, collapse this down.
[00:07:18] Okay, pop, pop is pretty easy. So we can say,
>> Mike North: We'll say,
>> Mike North: So we're gonna get the node,
>> Mike North: And then the new head equals next.
>> Mike North: We'll come back to that later, we still have to handle the case where the node might not be there. And in fact, we can just do this,
>> Mike North: Good enough, right? Early termination there. So pop looks okay. All right, now it's time for push. I wanna handle this one first, and we'll say items.forEach, and we'll pass in a nice little error function.
>> Mike North: I'm just gonna lean on the existing function that I have, right?
[00:08:28] I'm gonna route everything through the original push method up here. This is complaining, why? Duplicate function implementation. Okay, so now we're at a point where we have to tackle this overloading issue. Here's how we're gonna do it. One, two, so these are our two usable heads, this is the third one.
[00:08:59] And we'll use this union type here. It's a T or a T array, it's one or the other, only those two types will work. And again, this is the unaccessible one, the implementation not accessible directly.
>> Mike North: And in here we can say if, so we need a way to detect whether this is an array, and we're not gonna get instance of does not return a string array, it's gonna give us an object.
[00:09:29] But we can use an indirect method to do this like if, we can try this.
>> Mike North: Let's see if that works.
>> Mike North: Yep, looks like the type card works there. So instance of works as well.
>> Mike North: So in this situation, actually we'll need to return this in either case no matter what, so we'll leave that in place.
[00:09:59] Here we are going to push each of the items, and then down here our job is to push a single item. And once we implement this, I think we are probably gonna pass all of our tests. In this situation we're gonna say, so we've got a current head node, we'll say, create a new node, data: item.
[00:10:23] Actually, let's call this,
>> Mike North: Just to be really clear about it, item or array. So in this situation it's not smart enough to understand that the only other situation, it must be a single type T, right? Type of, it was able to figure it out. Here, instance of, it's a little bit more vague of a request, simply because there's so many prototypes out there.
[00:10:58] When we're saying type of, it knows that this is a union type, these are the two things it can be, and it can basically cross one thing out. So that's something that it can figure out. Instance of is a much harder question to ask. What if it's a sub-class of my array, right?
[00:11:13] If I had a subclass of my array,
>> Mike North: This would not work. So here we go, we're gonna have to say itemOrArray as T, we can do our past here. And then next is going to be the current head.
>> Mike North: And then now we make this node that we just created the head.
>> Mike North: And we end up passing all of the tests.
>> Mike North: So in summary, let's expand all of this out.
>> Mike North: So just starting from the bottom, we walked our linked list, right? We've got just a very simple linked list node. It's got the data of interest and an edge to the next node that we can use to traverse the list.
[00:12:18] Getting the length of a linked list is an expensive operation compared to an array cuz we don't know its length. This is the price we pay for the ability to add things in the middle really quickly with that reallocating. Popping a linked list is relatively easy. And here we see that the undefined situation is pretty simple to handle.
[00:12:41] We just we return a union type, and any one who pops must be prepared, right? You can't simply say, I've got a new list. Let so l equals new Stack of type number, and then say let x:number equal l.pop. It's basically gonna say, hey, be advised, TypeScript is telling you, remember this is not guaranteed to give you something, there are scenarios where this might be empty.
[00:13:18] And so now you have to handle that or you have to say I take the risk, right? I take the risk looks like this, look, because of how I'm using this, I know I'm always going to have something. But now you got some feedback telling you, reminding you that technically this can return undefined, and it's up to us to kind of handle that in some way.
[00:14:04] And then we have a union type here where we're saying, take either of these. So that is a broad enough type of argument to handle both of these cases here. And within the single function, we handle both of those cases using a condition. And instance of turns out to be a type guard that allows us, within this block statement, we know that item or array is definitely an instance of an array.
[00:15:30] And so it'll give you relevant location-based data about restaurants and things around wherever you happen to be. We're going to build the React component. And we're going to handle the asynchronous work that goes on using async and await, which is a way of writing asynchronous code in such a way that it almost looks like synchronous code.
[00:15:55] Before we use it, I'm going to help us get to the point where we can build something that's almost like our own async and await using something called the generator function, where we can pause execution at various points and wait for promises to resolve. By the end of that, you will all understand fundamentally how this looks.