Will I Survive These Advanced TypeScript Challenges?
Key Takeaways
The video demonstrates advanced TypeScript challenges, including creating custom types, type checking, and recursive string manipulation, using tools like TypeScript and utility types.
Full Transcript
absolutely blew my mind. Now, it doesn't actually look like that solved our problem. Actually, it looks like it's not quite working as we expect. I have always struggled with advanced TypeScript features. So, I decided to test my limits by going through various TypeScript challenges to see if I'm able to solve them, all while actually explaining everything that I'm doing step by step so that we can become better Typescript developers together. But what I didn't expect from this is that I was actually able to use a super niche TypeScript feature to solve one of these challenges in a way that the creator did not expect. Welcome back to WebDev Simplified. My name is Kyle and my job is to simplify the web for you so you can start building your dream project sooner. And I have three challenges I'm going to be tackling. The first is rated as easy, the second one is medium, and the third one is hard. And I'm honestly quite scared about this one because even the medium challenges I've looked at in the past are very, very difficult. Now, to understand how all this works, we're going to just use their sample data right here, which is just a warm-up. Essentially, at the very top, we have a question, and all we need to do is we need to solve whatever this question is. It's going to ask us to create a type that does something. In this case, we're just creating a type of string. That's really straightforward. At the bottom, you'll see here, this is the type we're going to create, whatever it is right here in this your code here section. And then we have some test cases that test to make sure that this is the correct type. So if we just type this as a string, you'll notice down here all of our test cases remove the errors, which means that they are currently passing. So let's go ahead and actually look at the very first example, which is this exclude one. Essentially, all we're doing in this example is we're reimplementing the built-in exclude type that's inside of TypeScript in our own way to figure out how it works. And this should be rated as easy, even though it's going to use some more advanced TypeScript features. Now, you will notice at the top there are some tints. For example, it's recommending like a union and built-in. These are like tags or categories, which sometimes can help you steer you in the correct direction. So if we look down here, you can see our different test cases. So we're excluding from A B C the value of A. So we're left over with just B and C. And here we have ABC. We're excluding A and B. And we should be left with just C. Now obviously I could just use the built-in exclude type from TypeScript. And if I do that, it's going to completely solve my problem. But of course, that kind of defeats the entire purpose. So let's go ahead and actually look at how we would create this exclude type. Essentially, we have this type T that we know has a bunch of different things. And we have another type called U that we know is a subset of our type T. So we just want to figure out what is in U and remove that specifically from T. Now in Typescript whenever you want to do like an if statement that's always going to be done using an extends keyword. So we can say T extends right here. And this allows us to actually dive into what is T. So for example, we should say T extend string. And now if T is a string, whatever is after our question mark is going to return. So for example, true. And if T is not a string, then this false section is going to be what's returned. And this works great when you have a single value for t. But when you have for example a union of values or maybe an object of values, then this extends keyword works a little bit differently and instead it applies to every single value inside of there individually. So it's checking every single value of essentially t and checking if those are all strings each one individually. So because of that we can almost think of this as a for loop looping over each value inside of t and then doing some type of if comparison on each one of those values. It's then going to return true or false for each of those individual values. And now we're going to get a return value that is essentially a bunch of different trus and false combined together. We can actually use this relatively easily to just put u here instead of string. So now we're looping through each value in t and seeing if it extends u. So essentially the very first time I loop through here I have a if I'm looking at this very first use case and I'm saying does a extend a. Well in this case extends essentially acts like an equal sign. So we are saying yes that is true. So it's going to return the value of true. The second time it's going to loop through on my value V. And you see B extends A, not true. So it'll return false. And finally here, it'll check C again. And C does not extend A. So it's going to return false for that. What I essentially want to do though is I want to take those true false and instead I want to convert it to a brand new union. That is going to be just the values that we're not excluding. So every time it returns true, I want to remove that value. So pretend it doesn't exist. That is where the never keyword in Typescript comes in. It essentially says this can never happen. And so it pretends that type doesn't even exist. And otherwise I want to just return that value as is. So I can actually just return T, which as we know in this case is each individual instance of that value T. This is what makes dealing with these types so confusing because T when we pass it in is essentially this full union. But when I use it in this extends, it's almost acting like a loop. And while I'm inside that loop, so inside this conditional statement, this T value instead of representing the full union only represents each individual value. So again, the first time we loop through, we get a a is equal to a. So we return never. Finally, B is not equal to A. So we return B as is. Same thing with C. C is not equal to A. So we return C as is. So the entire purpose of this my exclude, as you can see, all the test cases are passing. Is to really figure out that this extends keyword does so much more than just a simple if check. It can also be used for essentially a for loop that is going to map over those values and return to you a brand new value from them. And that is kind of the core of almost every single advanced TypeScript thing you write. And I'm sure in the other cases we look at, we're going to run into the exact same issues. Now, speaking of, let's look at our very next one here, which is going to be kebab case. So, in this one, all we want to do is replace the camel case or Pascal case strings and convert them to kebab case. So, here you can see we have foo barb baz is converted to fu-ashbar-bas. Essentially, anytime that I have a capital letter, I want to lowercase that letter and put a dash in front of it to give myself a kebab case instead of a camel case. Now, if we look down here at all the different examples we have, we can see some of the edge cases in place. So, for example, if we have a dash, it should just return as is. Empty string should not be changed. Anything that's not a capital or lowercase letter should stay the same. And here, for example, if we have like an underscore that stays the same. Literally, the only thing we're looking at is every time we have a capital letter, remove that capital, make it a lowercase, and put a dash right before that. That's essentially what we want to do in this particular challenge. So, when I think of this challenge, I first need to be able to recursively loop through this because I kind of need to loop through each letter of my Arab string here. And that's a quite difficult thing to do inside of TypeScript. There's no like for loops or recursion that you could do naturally. You have to use that fancy extends keyword to figure this all out for you. So we have s which we know is going to be an extr. I can even just come in here and say it extends string because it doesn't really make sense if it doesn't extend a string. And now what I can do with this s is I can say s extend and I want to try to essentially get each individual character of my value. So I want to get okay this f if this f is a capital letter I want to turn this to lowercase and then I want to get o and o and b I want to loop through each individual string as is. Well, this is something you can do with the infer keyword inside of TypeScript. So, what I can do in here is get a template string literal. This is actually kind of tinted to us up here with the template literal category that this is part of. So, here I have my template string and inside here I can actually put essentially variable values. In JavaScript, you could put like a variable for example like my name and that would put my name inside of this string. But in Typescript, what we could do is we can use the infer keyword and then give this a variable value. For example, I can infer a and now this a variable is just equal to whatever is inside my string. Now, in this case, it doesn't really make much sense to do this because I just have a string as is. But if I were to come in here and do like a followed by two O's, this A is going to be whatever is followed by two O's. So, in our case, it would be a capital F here because that's followed by two O's. And that's what A would equal in this particular string. Now, in my case, I want to loop through each individual letter one by one. So, we can actually stack two infers next to each other by doing this. We have infer A and we have infer B. Now, you may be thinking, okay, what happens in this scenario? because now we have two dynamic variables that we need to create. Essentially what TypeScript does is it tries to figure out what this first variable should be and it tries to make it as small as humanly possible. So in our case we just try to infer one single character of our string. So F in our case and then the final infer value in our list is just whatever is left over. So this B value is literally our entire string and A is just the very first character of our string. So in our very first example of fuar baz, essentially we have a is equal to just the capital f and then b is going to be equal to everything except for that f. So essentially this first value is going to be the minimal amount of content and the second value is just all the rest of our string. And we can even just call it rest to make it a little bit more self-explanatory. Now what I essentially want to do from here is I want to take that letter a because now I have an individual letter I can work with and I want to determine is this uppercase or lowerase. If it's lower case just leave it as is. But if it's uppercase, I want to change it to a lowercase and put a dash in front of it. So let's go ahead and try to actually do that. So in our first question mark section, this is essentially saying, okay, our string extends this section, which means our string essentially is long enough to have two different parts to it. So this is where our main code is going to go. And then after our colon here, we're just going to put s as the value we're returning because that essentially means that our string does not match this particular format, which means our string is too short to get multiple different values from it. Essentially, we have an empty string. And once we get to an empty string, I just want to return my string as is because in this middle here, we're going to do recursive code. And it'll make more sense why we're returning this string right here. So, let's come on down here. And I want to first of all, like I said, check to see is a uppercase or lowerase. And to do an if check, we need to use extends. So, we can say a extends. And I want to check to see if it's uppercase. So, I think I can just use the uppercase type here. And I can paste in a just like that. So, now I'm checking is a an uppercase value. If so, do something. otherwise do something else. So what we can do is for now I can just put never here and never here to get my type stuff correct. And now for this first case if we have an uppercase letter for a I want to make it lowercase and I want to put a dash in front of it. Otherwise if it's not uppercase I just want to return my value as is without doing anything complicated. So here I can just recursively call kebab case. I know that my a character is fine. So I'm just going to put my a character at the start of the string. Then I'm going to call kebab case and in it I'm just going to pass all the rest of the values that we didn't use. So essentially what I've done here is now I've taken this a character. I know that it's already lowercase so I can leave it as is and now I'm recursively calling this function by trying to get the next set of my string. So after I do this the first time f is the value for a and you know I have all my other stuff coming afterwards. The next time I call this function everything but the first character is going to be used because we're recursively calling it. So then I can come inside of here in the case where we have essentially an uppercase version of a. Well, I need to make this lowerase. So we can use lowercase to make that a lowercase value and we can put a dash in front of it because we need a dash in front because that's how you kebab case different values. So now just by doing that if we give it a quick save it actually looks like some of our test cases are passing which is great. So let's go ahead and actually see why some of these aren't passing. That should be for example this third test right here. I really think that this one should be passing because all we're using is fu-ashbar. There's not even any uppercase letters at all. So, it should just be essentially returning our string as is. Now, an easy way to type this inside of TypeScript is just to create a brand new type. We'll call it test. And we'll just set it equal to essentially calling that kebab case. And this will tell us what we get back. And if we hover over this, you'll notice it's actually giving us two dashes here, which is kind of interesting. So, essentially, whenever we encounter a dash, it thinks that's an uppercase letter. I'm guessing that's what's happening. So, uppercase actually is returning true for things like dashes. And it looks like even emojis are determined to be uppercase. So I think if we just swap this to be lowercase because if we get these false positives where like a dash is uppercase or uppercase and like a emoji is uppercase. I'm assuming we'll get the exact same false positives for lowercase and we can just swap our cases around. So here we just swap the order of these different values. And actually it does look like that solved it. If we look at our test here, you can see it is correct because now even things that aren't lowercase values, for example, they're just like dashes or underscores, those will be treated as if they were a lowercase value and passed along normally. So now that solves all of these different values. We only have these three that aren't working as we expect. So let's go ahead and we'll just copy over the text to see why these ones aren't working as we expect. And if we look at our test here, we can see, okay, it looks like the reason why is because the very first capital letter is getting a dash put in front of it when in reality it definitely should not be because it's the first character and the first character should just be lowercased with no dash added in front of it at all. Now, I think there are two different ways we can solve this. There's one way I can definitely think of off the top of my head that's easy, but in my opinion is a little bit hacky, and that is that we can use essentially an additional parameter inside of our kebab case function that is optional. So, you don't normally pass it in, but allows us to keep track of things. Often, it's called like an accumulator. So, like in a reduce function, you use an accumulator to keep track of things. It's the exact same thing in this particular case. So, we can use an accumulator of some form that just says like is first and we can set that to be true by default. We can say that it extends a boolean. So it's either going to be true or false. By default, it is going to be set to true. But every single time that we call this, we can just set this to false. So that now it is not the first value. So then essentially what happens is we can add another if check inside of here by just saying after that colon, we can say is first extends true. If it's the very first one, then we essentially want to just lowercase the letter and not push the dash in front of it. Otherwise, we want to put the dash in front of it. I think this alone will actually solve our problem. So here we just remove that dash. And now actually it does look like we've solved all of those individual cases. Now I'm personally not a fan of this approach because if I don't need to add these accumulator values on there, I don't like to because now all of a sudden I can come in here and just pass false and all of a sudden I break my entire thing even though that is a valid way for me to be able to pass that in. Now it is saying that type false does not satisfy the constraint true. That's because this thing is not passing. And if I just paste this up here, you can see I'm getting that dash back in the front because I'm overwriting essentially true false if this is the first time I'm accessing this or not. So I don't really like this particular way of doing things. And it also kind of goes against the spirit because they didn't have this as part of the challenge. So I want to try to solve this in a different approach. So let's go ahead and get rid of all of that is first stuff that we added. We'll get rid of this one and we'll get rid of those extra false checks in there. So now we should be left just with those original failures that we had. And I'm just going to again create that type test. There we go. So we at least have this thing that we can look at. Now I think the way that we're going to be able to solve this problem is by essentially assuming that the first character we don't really care about and instead we're going to be focusing on all of the later characters in our actual check here. So what we can do with our first character is just lowercase it no matter what because we always know that it needs to get lowercased. And then we can work on the next set of characters inside of our list after that. So, when we're doing our check to see if we have lowercase or uppercase values, I actually want to use the rest of the characters in our string. And unfortunately, lowercase checks to see if an entire string is lowercase. And uppercase checks to see if an entire string is uppercase. But we actually have another one called capitalize. And this checks to see essentially if the first character of each word is a uppercase letter. So, we can come in here with rest. And if our rest character extends the capitalized version, that means that they both start with a capital letter. And now we know we have a capital letter at the start of our string, we can take that capital letter and we can essentially make it lowercase and put a dash in front of it. So let's go ahead and we know that we want to essentially flop the order of these because we're now essentially doing our check in the reverse order. And in this case where we have a capitalize, I want to put a dash in front of essentially the rest of my character. So the dash is going to go right here. I always lowercase my first letter no matter what because it doesn't matter if it's uppercase or lowercase. The first letter should always be a lowercase value and we don't have to put any dashes in front of it. And then here with the rest of our values, I want to lowercase just the first letter. So I think we have not capitalized or something along those lines. That's like the opposite of capitalized. Let's come in here and actually see what it's called. Or maybe it's uncalized. There we go. Unc capitalized. Essentially, this is the exact opposite of capitaliz. it converts the first character of the string to lowercase. That's exactly what we want to do. So now in this particular case, we're lowercasing our first letter. And if we have a capitalized first letter in the rest of our string, we're putting a dash in front of it. And then we're lowercasing that one letter and just calling the rest of our code as is. Next, essentially, I want to do the same thing in this section down below. But I don't need to worry about uncalizing any letters or putting a dash. So I can just get rid of this uncalized content and get rid of that dash. Now let's go ahead and actually see what the code is being returned because obviously we have tons of errors down here and right now it's returning fu bar baz which it has a dash at the end though which is definitely not what we want. I think the reason that this is happening is because eventually we get to the point where rest is an empty string because our string gets down to one single character. So a is that one character and the rest of our string is an empty string. So we have a little bit of a problem there. Uh there's a few different ways that I think we could solve this, but the easiest I think is just by swapping this with uncalize here and then swapping the order of these because then essentially an empty string is going to be considered essentially uncalized. So then we can make sure that we have all of this code as is. And yeah, I think if we just save actually that will solve our particular problem. So this makes it so that empty strings follow this test case instead of following down here. I could also add in an additional extends where I just check to see if it's equal to an empty string as well. But this is a little bit easier because doing like an and check or an or check in Typescript requires you to do lots of turninary nesting which is obviously not ideal. Now technically this solves the problem that we're looking for. But I actually want to take this a step further by using one of the techniques that absolutely blew my mind the first time I heard about it. And that is the idea of tail recursion. If we take a look at our code, everything looks fine. But what happens if we try to execute this on a string that's very, very long. Let's just come in here and paste this down a few times. And you'll notice our code goes from working to getting an error. And if we hover over this, it essentially says type instantiation is excessively deep and possibly infinite. Essentially, if you recursively call a type too many times, usually it's like 40 or 50 times, TypeScript essentially says, I have no idea what to do. This is too deep, too much recursion. I'm just going to throw an error because I don't know how to handle this excessively recursive thing. So instead, what we need to do is we need to figure out a way to actually handle this recursiveness. And that is by using something called tail recursion which like I said blew my mind the very first time I heard about it because it essentially allows you to solve these particular problems relatively easily. And all tail recursion is it's a fancy word and there's a lot of fancy definitions but from my simple understanding essentially what we need to do is call the recursive function before we do any additional checks inside of our code and just immediately return that value. So in our case here we have our simple you know just extends check. That's perfectly fine but then we're doing another check down here. This is like our first main check. So we're doing a check to see if things are capitalized and then we're extending code in front of our kebab case. So we're returning an additional value that's not just our, you know, recursive version of this. So we have a bunch of stuff going on, additional checks, as well as additional data being added onto our kebab case, which means that it's no longer tail recursive because we're not just returning the value of this thing. We're instead returning the value plus something added onto the start of it every single time. So instead, we need to make it so that whenever we call kebab case, it's going to just return itself with no other stuff being added on. The easiest way for us to do that is to actually add one additional level of nesting at the very very top where we just call kebab case just like this with the rest of our code. And what we need to do is just say that infers some random type. Doesn't matter what it is. We're just going to call this our string. So we'll just say str just like that. Then we can put our question mark syntax. And we can come down here with our colon syntax as well. And for this one, we're just going to put never as the type because it should never be possible for this not to infer this string type right here. Now, I also need to make sure I come in here with an extend just like that. So, essentially, we're saying, hey, this kebab case extends inferring whatever it is. So, essentially, it extends itself is what we're saying. We're just taking whatever the value of this is and putting in a new variable called str. So now, every time we're using kebab case, we can replace that with str. And we can come in here, make sure I get that correct, and we can replace the exact same thing down here where we have this str. And that should actually solve our particular problems. I am noticing that we're getting a little bit of an error because this could be a string, number, boolean, blah blah blah, all these different things. So, we of course need to add another check that just says str extends string, which in our case, we know that it should extend a string, but TypeScript's not smart enough to do that. So, if it doesn't extend a string, we'll return never down here. Now, to make sure that this works, I essentially also need to copy over this fooarb baz a bunch of times to give us essentially the same number. And I copied it down a few too many times. There we go. And now you can see that this string that was too long originally is now able to be solved because of this tail recursion. We are making sure that every time we call kebab case, when it's being called recursively, it just returns immediately with no extra stuff being added on. You can see we call kebab case with whatever the rest of this is. No extra stuff is being added on. No other checks are being made. it just returns itself. So, it's able to essentially exit out. But since we still need to do additional things with this content, we just add a simple extends infer right here that essentially takes whatever this value was supposed to be, puts it in a brand new variable, and now we don't have to worry about that recursive stuff because TypeScript is smart enough to just essentially evaluate this section for the recursive portion, and figure out everything else on its own. Now, this wasn't actually part of the example itself, but I read about this when I was doing some TypeScript research for different projects, and it blew my mind, so I had to mention it to you because it's absolutely amazing. Now, we get to move on to what is hopefully probably going to be by far the hardest thing because it's in the hard category. And that is implementing a git function. And this is something that's really common in a lot of different libraries such as load dash. And it's a convenient way to be able to access nested data. So for example here if we have this type that is data which has foo bar it's got a value it's got count all these additional things inside of it. We just want to call get data and we can say like fu.bar.count and it's going to go into fu go into bar and give us the actual value for count or we can say you know we want to get the data for just hello and it's going to check this hello string. So it allows us to essentially recursively and nested go through an object and get all the different values from it. Importantly it says accessing arrays is not required in this challenge. So we don't have to worry about that which is kind of nice. we can instead just focus on objects. So if we take a look at our test cases, I think this will help inform what we actually want to do. We can see obviously if something doesn't exist, we return never. That makes a lot of sense. A single key just returns whatever that value is. Hello goes to world. fu.bar.count, as you can see, just goes through the object itself. This one, fu.bar, is actually the key itself. So we need to make sure we not only use dots for nesting further into our object, but also a key could have a dot inside of it. And we probably need to check that first before we do our nesting sections. So I think for the most part this isn't going to be too complicated for us to do. Yeah, you can see foo bar is returning the full object because foo bar gives us this whole object. So let's try to go ahead and think about what we want to do. T is going to be our data object. So I'm going to call this object just to give it a better name. And K, that's the key. We can just call it key. It's perfectly fine. Now if we didn't have to worry about nesting, I could just come in here and I could say object of key. And that's going to get us whatever the object at that key is. And obviously that solves these two particular problems right here. Now we are getting a little bit of type error because key cannot be used to index something that's a type of object. So we know that key is going to be a string. So we can say extend string just like that. And we know that this is going to be an object. So we could say extend record of property key and it's going to be unknown because we don't know what data type that particular thing is. So I think we're already on to kind of a good start. Essentially we want to get the value from the object and if we have a value from that we just can return it as is. So we can say if this extends any then that means that we have an value at that particular key. So we can just return that object key just like that. Otherwise we need to do something with that particular value. Now in my case I'm just going to return never to make sure our other test cases are still passing. And as you can see they are because we have something at that key. So it's returning what is at that key. Now this one right here should be returning never. And I don't quite know why it's not. So let's go ahead and take a look at it. We're going to convert this into a type just like that. We'll say t is equal to that. And we'll hover over what t is. And you can see the type here is unknown. And already I think I know the reason for this. And that's because right now when we do object of key, it's essentially getting us this generic unknown type. It doesn't know what it is. Every single time it's just returning our unknown type for us because it can't figure that out. So instead, we need to use some infer keywords here to actually make this work like we expect it to. So I can say object key extends infer and we'll just say prop because that's going to be our property value itself. So now we're just essentially taking whatever this is and putting in its own type called prop. Then what we can do is we can say prop extends any and then we're going to just return that value. Otherwise we return never. And again we'll return never down here. If for some reason this does not extend properly. Now it doesn't actually look like that solved our problem and we may not even need this prop. So, I'm pretty much going to get rid of all of this code and instead of extending any, I'm going to extend unknown because it seems like what's happening is when it can't find the key, it's returning unknown instead of returning never, which is a little bit unintuitive for my own thought processes, but it kind of makes sense because it could have a key at that value. It just doesn't know it. So, we can come in here and if it extends unknown, then we return never. Otherwise, what we're going to do is we're going to return our object of key just like that. And if we go ahead and we take a look at this, you can see this particular one is returning never, which is like we expect. But for some reason, our hello one is not working anymore. So we can say type h is equal to our hello one. And this one is also returning never. So it's essentially always returning never because everything extends unknown. I believe I think that's the thought process behind it. So I think instead of what we need to do here is just check if it's a key of this particular object. That's probably going to be a much easier way to do this. So we can say if our key extends key of object then that's going to be essentially the same thing. So we can swap the order that we were doing these in. Put this one as never just like that. And that actually did solve our particular problem. You can see this one is now never and this one is set to the correct type. So I was just trying to be a little bit fancy there with this any unknown stuff. We just really need to check is this a key that exists on the object. If it is a key that exists on the object then obviously what we need to do is just return it as is. Otherwise, instead of returning never, we actually need to do some more nesting because it could be the case where it's something that doesn't exist or it could be the case of where we just need to get the first character and essentially rerun it back through this particular function for getting. So now what I can do here is the same thing I did before to be able to get the character before the dot and all the characters after the dot. So I can say that my key extends infer and I specifically want to infer here the start of it. So we'll call this our key one. So we'll just say K1 and we're going to put a dot and we'll come in here with K2. So this is everything that's after. We'll call this rest because it's a little bit easier. And then this is going to be our internal key that we want to use. Now we need to come in here. We'll put never for both of these just so we can get some type safety stuff going on. I need to make sure my infer keyword is inside here. Whoops. There we go. Now we give that a quick save. And that looks great. So now if our key extends this particular thing, we can now essentially check to see if our object key is essentially a key. So now we can say here essentially the exact same thing we did up here K1 extends key of object. If that's the case then what we want to do is we want to call get and we want to pass it in essentially all of the remaining keys and that's going to be for our key section and our object is just going to be object of K1 and we want to return the rest of our keys afterwards. There we go. Now in the else case then I think we can safely return never because if this K1 is not a key of the object then it's essentially going to be never. So we can just come in here and we can put never at the end there. And now we're getting a little bit of an error here. It says object K1 does not satisfy the constraint record property key unknown. That technically makes sense because it doesn't know what this particular type is. I think we can just get rid of this entire extend section up here and it's going to be fine. And actually it looks like that entirely solved the problem that we're going with. I thought we'd have to go a little bit more complicated, but it makes sense that this is solving it like we would expect it to. Now, I think we're going to run into the same error where we can't do our like 50 levels of nesting. But the nice thing about this is the 50 levels nesting is for each dot. So, we would have to have an object with over 50 different levels of nesting that we go through. But, we can kind of look at this to see if there's maybe a different way that we can go about solving this particular problem. And I think we can actually simplify this slightly because essentially we don't really care if K1 extends the key of the object at all. if it doesn't extend it, it's going to return never. But if we just try to access object K1, it's going to return never if that thing doesn't actually exist. So instead, what we can do is I think we can just put our recursive call directly right here and get rid of this entire nested section. If we give that a save, actually, it looks like it's not quite working as we expect because yeah, K1 can't be used to index this because we don't know that it's a key of it. So that makes sense. We can bring it back to what we had before because this does work like we expected to. Now, I am curious to see if this will work with arrays though. So let's come in here and actually like add in an array. We'll just say r is going to be an array that has a value of 1 2 3 just like that. And let's come and just do a simple test. We'll say is it foo r and that's going to be of zero. So I think we just need to do zero and that should be equal to one just like that. And actually it does work with arrays as well. If we come in here with two that should be set equal to two and actually sorry one index one should be set to two. So yes this does work with arrays which is really cool. I think we could even do nested objects. For example, inside here I could say bar is actually equal to one now. And what I could do is I could say array of0.bar that should be equal to one. And that test case does pass as well. And that's essentially because this key of property essentially arrays are objects inside of JavaScript. So it's just able to determine okay the key here is going to be two for example or or sorry not two it's going to be an index of one or zero in our particular case. So essentially, it's just working like that index accessing of arrays works exactly the same as accessing the actual properties on objects. So it's nice that this actually works for arrays, even though it's not part of the challenge. Now, if you enjoyed this video, you're going to love these other two videos where I create a full React router from scratch with full type safety and I implement internationalizations with massive type safety unlike any library I've ever seen before. Both those videos, they're going to be linked right over here for you. With that said, thank you very much for watching and have a good
Original Description
FREE TypeScript Utility Types Cheat Sheet: https://webdevsimplified.com/ts-utility-types-cheat-sheet.html
Using TypeScript is relatively easy, but writing complex TypeScript types is incredibly difficult. In this video I will be challenging myself with 3 different levels of TypeScript challenge (easy, medium, and hard) to see if I can conquer these ever increasingly difficult TypeScript problems.
📚 Materials/References:
FREE TypeScript Utility Types Cheat Sheet: https://webdevsimplified.com/ts-utility-types-cheat-sheet.html
🌎 Find Me Here:
My Blog: https://blog.webdevsimplified.com
My Courses: https://courses.webdevsimplified.com
Patreon: https://www.patreon.com/WebDevSimplified
Twitter: https://twitter.com/DevSimplified
Discord: https://discord.gg/7StTjnR
GitHub: https://github.com/WebDevSimplified
CodePen: https://codepen.io/WebDevSimplified
⏱️ Timestamps:
00:00 - Introduction
00:50 - How the challenges work
01:22 - Challenge #1
05:04 - Challenge #2
20:51 - Challenge #3
#TypeScript #WDS #TSChallenges
Watch on YouTube ↗
(saves to browser)
Sign in to unlock AI tutor explanation · ⚡30
Playlist
Uploads from Web Dev Simplified · Web Dev Simplified · 0 of 60
← Previous
Next →
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
Introduction to Web Development || Setup || Part 1
Web Dev Simplified
Introduction to Web Development || Understanding the Web || Part 2
Web Dev Simplified
Introduction to HTML || Your First Web Page || Part 1
Web Dev Simplified
Introduction to HTML || Basic HTML Elements || Part 2
Web Dev Simplified
Introduction to HTML || Advanced HTML Elements || Part 3
Web Dev Simplified
Introduction to HTML || Links and Inputs || Part 4
Web Dev Simplified
Learn Git in 20 Minutes
Web Dev Simplified
5 Must Know Sites For Web Developers
Web Dev Simplified
10 Best Visual Studio Code Extensions
Web Dev Simplified
Learn CSS in 20 Minutes
Web Dev Simplified
How to Style a Modern Website (Part One)
Web Dev Simplified
How to Style a Modern Website (Part Two)
Web Dev Simplified
3D Flip Button Tutorial
Web Dev Simplified
How to Style a Modern Website (Part Three)
Web Dev Simplified
Animated Loading Spinner Tutorial
Web Dev Simplified
How to Write the Perfect Developer Resume
Web Dev Simplified
Animated Text Reveal Tutorial
Web Dev Simplified
Learn Flexbox in 15 Minutes
Web Dev Simplified
Custom Checkbox Tutorial
Web Dev Simplified
Start Contributing to Open Source (Hacktoberfest)
Web Dev Simplified
JavaScript Shopping Cart Tutorial for Beginners
Web Dev Simplified
Responsive Video Background Tutorial
Web Dev Simplified
1,000 Subscriber Giveaway
Web Dev Simplified
How To Prevent The Most Common Cross Site Scripting Attack
Web Dev Simplified
Transparent Login Form Tutorial
Web Dev Simplified
The Forgotten CSS Position
Web Dev Simplified
How to Code a Card Matching Game
Web Dev Simplified
10 Must Install Visual Studio Code Extensions
Web Dev Simplified
Learn CSS Grid in 20 Minutes
Web Dev Simplified
Learn JSON in 10 Minutes
Web Dev Simplified
10 Essential Keyboard Shortcuts For Programmers
Web Dev Simplified
What Is The Fastest Way To Load JavaScript
Web Dev Simplified
Differences Between Var, Let, and Const
Web Dev Simplified
How To Install MySQL (Server and Workbench)
Web Dev Simplified
Learn SQL In 60 Minutes
Web Dev Simplified
How To Solve SQL Problems
Web Dev Simplified
What Are Design Patterns?
Web Dev Simplified
Null Object Pattern - Design Patterns
Web Dev Simplified
Your First Node.js Web Server
Web Dev Simplified
How To Setup Payments With Node.js And Stripe
Web Dev Simplified
How To Learn Any New Programming Skill Fast
Web Dev Simplified
Asynchronous Vs Synchronous Programming
Web Dev Simplified
JavaScript ES6 Arrow Functions Tutorial
Web Dev Simplified
Are You Too Old To Learn Programming?
Web Dev Simplified
JavaScript Cookies vs Local Storage vs Session Storage
Web Dev Simplified
JavaScript Promises In 10 Minutes
Web Dev Simplified
Builder Pattern - Design Patterns
Web Dev Simplified
JavaScript == VS ===
Web Dev Simplified
JavaScript ES6 Modules
Web Dev Simplified
8 Must Know JavaScript Array Methods
Web Dev Simplified
CSS Variables Tutorial
Web Dev Simplified
JavaScript Async Await
Web Dev Simplified
How To Choose Your First Programming Language
Web Dev Simplified
Easiest Way To Work With Web Fonts
Web Dev Simplified
Singleton Pattern - Design Patterns
Web Dev Simplified
Responsive Navbar Tutorial
Web Dev Simplified
CSS Progress Bar Tutorial
Web Dev Simplified
Learn GraphQL In 40 Minutes
Web Dev Simplified
What is an API?
Web Dev Simplified
Learn How To Build A Website In 1 Hour!
Web Dev Simplified
More on: Agentic Coding
View skill →Related Reads
📰
📰
📰
📰
Memoization useMemo()Explained: Why React Doesn’t Need to Repeat the Same Work
Medium · JavaScript
Why Dark Mode Should Not Be a Second CSS File
Dev.to · Hasan Sarwer
Frontend-Only SaaS: The Rise of Static Utility Sites
Dev.to · yobox
Why I Built Yet Another JavaScript Date Picker
Dev.to · RollDate
Chapters (5)
Introduction
0:50
How the challenges work
1:22
Challenge #1
5:04
Challenge #2
20:51
Challenge #3
🎓
Tutor Explanation
DeepCamp AI