Video bnTAOB3P6nM
AI Summary
This video teaches advanced TypeScript type features, including extends, infer, template literals, and mapped types. Starting from basic concepts, it progresses to complex conditional types and type transformations, enabling viewers to read and write sophisticated TypeScript types.
By the end, viewers will understand and write complicated TypeScript types, covering extends, infer, and other advanced features.
Extends in classes means the child class contains all properties of the parent class (superset).
Interfaces can extend other interfaces, combining properties. Types use '&' (intersection) instead of extends.
In generics, 'T extends string' means T must be a superset of string (string or more specific).
Ternary syntax: 'T extends string ? true : false' returns different types based on condition.
Infer extracts a type from a larger type, e.g., inferring array element type or function return type.
Use template literals with infer to parse string patterns, e.g., splitting 'key:value' into key and value types.
Loop over object keys using 'in keyof T' to transform properties, e.g., making all values undefined or adding getters.
Use '-' to remove readonly or optional modifiers, and '+' to add them in mapped types.
In mapped types, use 'as' to rename keys, e.g., 'get' + Capitalize<P> to create getter names.
Mastering extends, infer, template literals, and mapped types empowers developers to create powerful, reusable TypeScript types that can parse strings, transform objects, and enforce complex constraints.
Clickbait Check
95% Legit"Title accurately promises teaching advanced TypeScript types, and the video delivers exactly that."
Mentioned in this Video
Tutorial Checklist
Study Flashcards (9)
What does 'extends' mean in the context of generics (e.g., T extends string)?
medium
Click to reveal answer
What does 'extends' mean in the context of generics (e.g., T extends string)?
T must be a superset of string, meaning it contains all properties of string and possibly more.
03:41
How do you write a conditional type that returns true if T is a string, false otherwise?
easy
Click to reveal answer
How do you write a conditional type that returns true if T is a string, false otherwise?
type IsString<T> = T extends string ? true : false;
06:05
What does the 'infer' keyword do in TypeScript?
medium
Click to reveal answer
What does the 'infer' keyword do in TypeScript?
It allows you to extract and assign a type variable from a larger type within a conditional type.
09:38
Write a type that extracts the element type from an array type using infer.
hard
Click to reveal answer
Write a type that extracts the element type from an array type using infer.
type Unpack<T> = T extends (infer U)[] ? U : never;
10:02
How can you parse a string like 'key:value' into an object with key and value types using template literals?
hard
Click to reveal answer
How can you parse a string like 'key:value' into an object with key and value types using template literals?
type Split<T> = T extends `${infer K}:${infer V}` ? { key: K, value: V } : never;
16:04
What is the syntax for a mapped type that makes all properties of T undefined?
easy
Click to reveal answer
What is the syntax for a mapped type that makes all properties of T undefined?
type MakeUndefined<T> = { [P in keyof T]: undefined };
19:47
How do you remove the readonly modifier from all properties in a mapped type?
medium
Click to reveal answer
How do you remove the readonly modifier from all properties in a mapped type?
type Mutable<T> = { -readonly [P in keyof T]: T[P] };
22:16
How can you rename keys in a mapped type, e.g., prefixing with 'get'?
hard
Click to reveal answer
How can you rename keys in a mapped type, e.g., prefixing with 'get'?
type Getters<T> = { [P in keyof T as `get${Capitalize<string & P>}`]: () => T[P] };
23:38
What is the difference between extends in interfaces and the ampersand (&) in types?
medium
Click to reveal answer
What is the difference between extends in interfaces and the ampersand (&) in types?
Interfaces use extends to combine; types use & (intersection) to achieve the same effect.
02:46
🔥 Best Moments
Conditional Types Explained
Clear explanation of using extends in ternary to create conditional types, a fundamental advanced pattern.
06:05Infer Keyword Power
Demonstrates how infer can extract types from complex structures, enabling powerful type utilities.
09:38Key Remapping with 'as'
Shows how to rename keys dynamically using 'as' and template literals, a clever technique for creating getter types.
23:38Full Transcript
Download .txt[00:00] By the end of this video, you're not only going to understand how to read complicated types like this, but I'm also going to teach you the skills you need to know to actually be able to write these types yourself. I'm going to be going over the advanced use cases for Extend, how Infer works,
[00:14] as well as many other advanced features in TypeScript. I'm going to be starting with the most basic, moving all the way up to super advanced stuff at the end, so that you can be confident in writing these really complicated TypeScript types in your own project.
[00:27] 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 the first thing I want to talk about is the extends keyword, because this is a keyword that has lots of different meanings through JavaScript and TypeScript,
[00:40] and we need to kind of clear up what all those different meanings are. So the first thing most people think of when they think of extends, which is probably something you're used to, is extends in the form of classes. I have an animal class, and I have a dog class, and this dog class extends my animal class.
[00:52] And essentially, all that means is that my dog contains all the same stuff that my animal contains, because it is an animal. So you can see here, I have dog which has this name that is part of it. I can say dog.name to get the name of the dog, and it has this constructor that takes the name
[01:06] because it's inheriting that from the animal. This is kind of the normal use case of Extend that you see in JavaScript as well as many other languages. This is only one aspect of how the Extend keyword works. You can also use it when dealing with specific types as well.
[01:19] So what I can do is I can create two different types, and in our case we're going to be using interfaces because interfaces allow you to extend while actual types don't. I'll show you the difference between them in a second. So we can say interface A right here, and this is just going to have a name, which is a string.
[01:32] And we'll say interface B. This one right here is going to have an age, which is a number. There we go. So now I have two different things, interface B and interface A. And what I can do is I can say that interface B is going to extend interface A.
[01:45] So now B contains everything from A as well as its own age. So we can see here that B essentially is a string and a number. So to really show what that's like, I can just say const b, type that as b, and now you can see age and name are the only two properties that I am able to define on this particular thing because it contains both all of the properties from a and all of the properties from b.
[02:07] So in this case, extends works very similar to how it did with classes. Essentially I'm saying b is an extension of a, it contains everything inside of a. If you want to think mathematically, you can essentially say that b is a superset of a.
[02:19] It contains everything inside of A plus potentially more things on top of it. And you can extend multiple different things. For example, I can have another interface. I can call this interface C with another name property. And now I can extend both of these different properties.
[02:32] You can see I extend A and C. And now I need to define this name2 property as well because that's coming from C. So now I'm saying that B is a superset of A and C. It contains everything from A, everything from C, and then potentially other things on top of that.
[02:46] Now if you were using types instead of interfaces, So for example, I just change all these interfaces to types, and I change it by code so it's actually valid code. Instead of doing extends here, I need to do something slightly different. I just use the ampersand symbol, because I'm saying that B is a combination of A and C and this number section down here.
[03:03] And you can see I get the exact same result. Everything is working just fine as before. So the ampersand symbol essentially replaces extends when you're using types. And this is where a lot of the confusion comes in when people start dealing with extends in the more advanced use cases we're going to be talking about.
[03:16] because they think of extend as like an ampersand symbol. But it doesn't really mean and. Instead, it means a superset. B contains everything from these two elements. It doesn't necessarily mean it's B and A.
[03:28] It's saying B is equal to all of the things from A, all of the things from C, and then anything in addition on top of that. So one place where this distinction is really important is when you're dealing with generics because this is kind of the first advanced use case for extend.
[03:41] So let me just create a type here. We're just going to call it generic. And this generic type is going to take in a parameter T. Now if you're not familiar with generic types, I have a full video, I'll link in the cards in the description for you to check out. And if you want to really learn everything there is to learn about TypeScript from absolute
[03:54] beginner to all the way super advanced stuff, I have a full TypeScript course I'll link down in the description for you as well. We have this generic type T, and we can just come in here and we'll say that the name is Type of T. So whatever this T type is, it's going to be the same type as my main property
[04:08] down here. Now oftentimes something that you'll see inside of these generics is they'll have an extends keyword acronym as well as some types. So in this case we'll say extends string. And we're not necessarily saying that t is t and string because that doesn't really make sense
[04:21] because t is not a value. It's whatever it wants to be. What we're saying in this case is that t is a superset of string. It contains everything that string contains and then potentially more stuff on top of that. That essentially means that t is a string or something that essentially
[04:36] extends a string. It is a string plus more. So to use this particular thing we can say here that we want to have a variable, we'll say const a, we'll give it that generic type. And for the generic type, we need to make sure that we pass in a string as we have that generic type.
[04:51] So it could be a hard-coded string like this, it could be the type string like this. I can even take this a step further by creating my own custom type. And this custom type is essentially going to have some properties, we'll say that it's going to have a main property on it.
[05:04] Actually, let's make it age so it's a little bit more clear what's going on. And then we're going to say that this is also going to be a string. So essentially this custom type is everything that a string is plus it has an age property, so I can actually use that inside of here because it is essentially a superset of string
[05:18] because I'm taking everything from string and putting that inside my custom type. So this extends keyword when used inside of a generic like this where you have it inside these angle brackets. What that means is P is a superset of string, and essentially that just means when you pass in your generic value of T or whatever it's going to be,
[05:33] it needs to at least contain all the properties of the thing that it's extending. This is really useful to essentially narrow down and say, you know what, this must be a string, or this must be an object, or this must be a function. So if I type in something like number here, I'm going to get an error because now I'm passing in a number which does not satisfy the constraint of being a string.
[05:50] Now this is again something you've probably already seen before, but this final kind of way that you can use Extents is the complicated way that we saw used all over the space in here. And that is essentially by doing ternary if statements. So essentially the ability to do a conditional type form, it's stated inside a type string,
[06:05] is also managed by this exact same extend keyword. So generally this is again something you're going to do with generic types, so we use this exact same generic here. And what I specifically want to do is I want this generic to return true if I have a string passed in, and I want to return false otherwise.
[06:20] So you can play t extends, and I'm going to say string. So again I saying does t is it a superset of strings Is it at least as big as my string If not it includes additional things So is this of type string or beyond If so then what I do is return whatever comes after my question mark a normal ternary
[06:37] So in our case, we'll say true. Otherwise, if this extend statement is false, it's going to do whatever is after this colon right here. So in our case, we will say false. So now we're not specifically saying that this t must extend a string.
[06:49] That's what we would do instead of here. If we said this, then T must extend extreme. What we're essentially saying is T can be anything. We don't care what it is. If it does extend extreme, then we're going to return true as our type.
[07:01] Otherwise, we return false as our type. If we hover over A, you can see that A is typed as false. Well, if I pass in a string here, you can see that A has been typed to true. And if I go back to where I had my custom string here real quick,
[07:13] here we go, and I pass in my custom string, since that is an extension of a string, is still going to return true as a type for A. This ability to essentially perform if statements and conditionally choose one type versus another type is really powerful inside the TypeScript.
[07:27] If we take a look at this other file, you'll notice that we use this all over the place. You can see here, I'm doing an extension, and then I'm doing some code. Otherwise, I'm doing another extension. I'm nesting my ternaries, and then doing something else. And again, nesting my ternaries further and further.
[07:39] And that is one downside to TypeScript, and there's no way to write your if checks without using a ternary. So you often end up with many, many levels of nested ternary in TypeScript, which makes the code more intimidating and difficult to read.
[07:51] But if you realize that all this is essentially saying is, if parantite is a number, do this code. Otherwise, if parantite is a plural, do this code. Otherwise, if it's a date, do this. If it's a list, do this. If it's enum, do this, and so on.
[08:03] All we're doing is just one single if check at a time, do one thing. Otherwise, do something else. Do one thing. Otherwise, do something else. It's a very common pattern that you're going to see inside a TypeScript. And we don't have to return true, false here. we can return whatever we want.
[08:15] For example, let's say that we're going to have an object, and this is going to have a property on it, and the property of this is going to depend on the type of this T here. So if T extends a string, then we want the property here to be a value of string.
[08:28] Otherwise, what we want is the property value to be number. Now what happens inside of here is my property value is a string right now because this extends a string. If I type in something else, for example, void is not a string,
[08:41] this is not a string at all, Now you can see this is giving me an error because this should be a number, and now it's successfully working. You can see here I can use that to define different types based on different things, and I can even mess this further if I wanted to. For example, I want to check to see if it extends a number, and then I'm going to use a number type.
[08:58] Otherwise, I'm going to use some other types. I'm just going to come in here, fix that bracket, and let's say that we're going to set undefined as the final type for this property. Now you can see here, by passing a number, this correctly is a number, by passing a string,
[09:10] this is going to ask me for a string. And if I pass on something other than those, for example, null, it's going to expect this to be undefined. It's the only value that allows me to pass to it. So I can do multiple different layers of nest in as deeply as I want
[09:23] or as shallow as I want. But again, that just makes your code harder to read. But unfortunately, inside of TypeScript, there's no way to do these if checks without these question mark syntaxes. Now, the next thing that I want to talk about is the infer keyword, which essentially extends upon the extends keyword, no pun intended.
[09:38] But the inferred keyword is, I think, one of the most powerful keywords inside a type strip when it comes to creating complicated types. And if we look down here, you can see that I heavily use this inferred keyword inside of this type to do really powerful things.
[09:50] So let's take a look at what that inferred keyword does real quick before we start diving into some of the more complicated things we can do. I'm going to clean this code up slightly, just get back to where we have the generic of T. Now this inferred keyword is only useful in conjunction with extents.
[10:02] So I can say here, T extends. And what I want to do is I want to extend something slightly more complicated. I'm going to say that I want to extend an array of values, and we could say, for example, it's going to be an unknown array because I don't know what those types are.
[10:14] But what I want to do is I want to figure out what the type of those values are, and I want to do something with that type. So what I can do is I can say infer, and then give it whatever type I want, for example, a. And now what's going to happen with this is I'm saying, hey, does t extend an array of any type?
[10:28] It doesn't matter. If it does, then I'm going to set a new generic variable, a, to whatever the value of that array type is. And I can, for example, return a here, or I could return never, for example, if it doesn't infer an array.
[10:43] So essentially, if this generic type that's passed in is an array, then my type is going to return whatever is inside that array. So if it's an array of strings, it will return string. Otherwise, if it's not an array, it returns never. So let's take a look at that in a quick example here.
[10:57] I'll just pass in an array with some strings inside of it, just like this. If I hover over a, you can see the type of this is going to be those two specific string values, values because I'm hard coding the values in. Now we're going to come in here and let's just say we're going to create an array of strings,
[11:09] something like that, and then I'm going to take B, I'm going to use that as my type here, type of B, there we go. Now I hover over A, you can see I get string as my value being returned. If this array also had a number in it, I will get string or number being returned.
[11:23] But if B is, for example, just a string, it's not an array of any form, if I hover over this you can see I get never being returned. The refer keyword is a really useful keyword for being able to dive further into a type and then doing things with that specific type.
[11:36] I can even go further and extend upon that. So I can say if A extends string, then I want to do something else. I'm going to say it's going to return number, otherwise I will return never. So now, if I have an array of strings, for example,
[11:50] and to make sure that my code is perfectly working fine, I need to add another never onto the end here. So essentially what's happening is I first check, hey, does T extend an array? If it does, run this code. if it doesn't return number. Next I'm checking, okay, what is the thing inside my array, A,
[12:05] that's what I called it, I inferred that. If that extends a string, then I want to return number as my type, otherwise I return never. So now you can see here when I hover over A, it returns a type of number because my string, or my B value, is an array with strings inside of it. That's the only way I get to
[12:20] a specific value right here. This is an incredibly powerful tool inside a type script, and it's used in a lot of built-in types. For example, I'll just come in here and I'll say type A is equal to, and I want to get whatever the return type of fetch is, just like that, and I need to put type of here. So I'm just
[12:37] taking the fetch function and getting the return type, and you can see here I'm getting a promise that returns a response. That is the thing that fetch returns. If you hover over this, you can see that the return is a promise that returns a response. Well, we can actually build our own return type
[12:49] very simply I just going to say custom return type and this custom return type function is going to use that fancy syntax we talked about We going to come in here with a P First of all I just need to make sure that this is a function So to make sure it a function we can say it going to take any arguments
[13:03] So this is going to say, literally, this takes any arguments, and it's going to return to us any value. So this is a function that takes any arguments and returns any value. I don't care what it is. Then what I want to do with this type is if T extends, essentially, a function,
[13:17] which we already know that it does, what I want to do is I want to get the return type of it. So essentially I'm going to take this function syntax just like we had before, and I want to get this part of it. I want to get just the return type. So I'll say infer R. I'm saying I want to infer whatever this is and use that particular type.
[13:31] Then I can say return R or return never. Now what this little bit of code right here is doing is essentially I'm saying that T must extend a function in some form. Then what I'm doing is I'm getting the return portion of that function and returning that, otherwise I return never.
[13:44] So if you hover over A, by default, it says a promise of a response. It's a normal return type. If I use my custom return type and paste that in, you can see we get the exact same value, promise, which is is or swap. And that's, again, because we're using this inferred keyword to figure out all of this information for it.
[13:59] It's inferring whatever the return portion of this type is. I don't know what this is. It literally can be anything. All I'm doing is saying whatever this type happens to be, put that inside this variable called r, and then return that variable. And I can do other things with that I can, you know, throw in and extend on top of here and so on if I want to, but I don't have to.
[14:15] I can even do the same thing with awaited. There's a built-in property called awaited and all this does is it takes a promise and it gives you whatever the return of that promise is. In our case, you can see we get response here because it's a promise that returns a response, so just look at the response portion.
[14:29] Well, I can create my very own type called custom awaited and this is going to work very, very similarly. We can come in here with our key and we can say that this is going to extend a promise of any, just like that, if we want to make sure it returns a promise.
[14:44] I believe TypeScript doesn't force you to do this, so we'll actually leave this off and just leave it as T to make it a little bit easier. Then we can say T is going to extend that exact same promise syntax, and we know that this can be any value inside here, but I want to get what this type is, so we just throw in an assert.
[14:58] We can call it whatever we want, in our case we'll call it P. So we'll say P, and then we're going to say that it's going to return by default T. So essentially, if it's not a promise, it's just going to return itself. So now you can see this returns a response with the normal awaited.
[15:11] If we start in R, awaited, and we hover over this, you can see it also returns response. It works exactly the same as the built-in function. So this infer keyword is incredibly powerful, and when you're writing complicated TypeScript types, it is probably the most powerful tool that you're going to use,
[15:24] and it can do everything from simple stuff like this all the way to complicated code like what's going on inside of here. And this is specifically using a trick inside of TypeScript, which is template literals inside of TypeScript, which is the next thing that I want to talk about for an advanced feature.
[15:37] You notice inside of my extends here, you can see S extends its strength. So I know that s is a string of some form. Now I'm checking to see what the actual syntax of that string is. I'm saying, okay, this string is some string followed by an opening curly brace,
[15:52] followed by a variable that I'm creating called param, followed by a closing curly brace, followed by another parameter that I'm creating called rest. Now this may look a little complicated in this syntax, so let's take a look at a simpler version to understand exactly what's going on.
[16:04] Imagine I have a string, for example, that looks like this. It's going to have a param, and it's going to have a value. There we go, super simple. It's going to have a frame on one side and a value. We can call it keyValue, so it's super straightforward to understand what's going on.
[16:17] This is what we want to create a type for. I'm going to create a type that's going to be called keyValueSplitter. This is a generic type, and we know that key must be a string, because I'm going to be passing in a string. And all I want to do is I want to return an object, which is a key, and a value,
[16:32] and it's going to be the key is going to be equal to this part of my string, and the value is going to be equal to this part of my string. So to do that, we can really easily use the extends keyword. So we can say key extends, and we already know that it extends a string,
[16:44] but I want to check a string in this particular format. So what I'm going to do is I'm going to come in here, I'm going to say that we're going to have our key, which we're going to infer as k, followed by the next section, which is going to be our value, so we're going to infer that as v.
[16:57] So essentially all I'm doing is I'm saying, hey, inside of my code somewhere, I have this particular string portion, which I'm going to call k, followed by a colon, followed by some other string portion called v. I don't care what it is, it just comes after the colon.
[17:09] If my string has that essential format, then I want to return the object, which is going to be key, which is a type of key, and value, which has a type of value. I believe I just called those K and D, just like that.
[17:21] Otherwise, if I don't have anything in that format, I'm just going to return never for this. So we can actually see this in action. We'll say type A is equal to that particular type, and I'll pass in this exact string. And you'll notice when I hover over A, you can see my key has the value of key and my value has the value of value.
[17:39] If I change this to, for example, say name, and I change this to say Kyle, you can see that my key is name and my value is Kyle. It's perfectly updating based on what's on either side of my colon. Now, if I pass in a string that doesn't have a colon, for example, I put in like an underscore here,
[17:54] you can see this just returns me to type up never. Because it doesn't meet this particular syntax, there's no colon inside my string, So it essentially says this does not match, I return never and do nothing else. So the combination of extends, the inferred keyword, as well as this template literal syntax
[18:08] can allow you to do some really complicated and cool stuff with texture, which makes some of the really complicated stuff, such as the extract parameter options function, which is inside my internationalization crash class. I'll link the video for you in the cards in the description because it goes over all this code in quite a bit of depth.
[18:22] But essentially you can see I'm able to use all these different things in conjunction to create these really complicated types that allow me to extract things out of these actual things. So I can extract a bunch of information about parameters that are wrapped inside of these curly braces
[18:34] and have some colons in between them. So you can see I'm taking these very small pieces, throwing them all together into one more complicated type, as well as another more complicated type, to give myself some really, really impressive types of code
[18:46] that can do really cool and important things. Now the final advanced feature I want to talk about is going to be one that deals with looping over objects. So let's create a really simple object type here. We'll just say that this is going to be O, and this is going to have two different properties,
[18:59] a name, which is a string, and an age, which is a number. And I want to be able to create a new type, which takes this O type and conforms it to do different things with it. So what I can do is I can call this my new type. I'm going to set this equal to an object, and with this particular object, I want to
[19:14] do things based on this O type up here. But of course, I'm going to be doing this in a generic way, because if I already know what my O type is I could just obviously come in here and say name and do something an agent do something but I want this to be generic so that no matter what this type up here is I can do whatever I want with it So we just coming here with a generic type of P just like that and then what I want to do is I want to say that I want to get all the properties
[19:33] inside of that key of T. So I'll call them P, you can call it whatever you want, and you can say in, key of, and then T. So essentially what this little bit of code is doing inside of here is it's going to get every single key inside of my type of T.
[19:47] So if we pass in O, so we could just say const A is going to be new with that O type, just like that. What essentially I'm doing is I'm saying, okay, all of the keys inside of O, for each one I'm going to call that particular key P.
[20:01] So I have name and age are going to be passed in. And this doesn't look like it creates a loop, but this actually creates a loop for us. And what I can do is I can use each of those values and redefine them to something else. So for example, I can redefine them as undefined.
[20:14] So now every single value that was inside of my O object is going to be redefined as undefined. If I hover over size A here, it doesn't actually give me the type, but if I look inside of here at age and name, you can see both of these are set to undefined.
[20:27] If I specify undefined, they work, but if I try to put a number inside of here like it was before, I get an error because this must be undefined. You can see the type is specifically undefined. But I can actually do something specifically with the actual key themselves if I want to.
[20:41] So for example, I could use tp like this. I'm saying essentially give me the parameter from t at that key. What this does is it doesn't change anything at all. If I look at my a object, you can see my name is a string and my age is a number.
[20:56] Everything is still exactly the same as before because by doing this, I'm just accessing whatever this type is right here and not changing it at all. But I can take this as set further. I can say if it extends a string, well, then I want to change it to a number.
[21:08] Otherwise, I don't want to do anything and I just want to leave the type as P as before. So now I'm essentially converting all the strings and changing them to be whatever that type is supposed to be. So it's either going to change from a string to a number or it's going to stay what it was before.
[21:21] So now you can see name is supposed to be a number because it's being changed from that string type. I can even change this by just putting P over here to kind of show you exactly what P is. Essentially, all this is doing is just taking whatever my P is and using that as a value.
[21:33] So age must specifically be set to the value of the age and name must be set to the value of name because that's what I'm essentially telling them that they need to be. So this is useful if you essentially want to remap some types inside of an object, but the place that this becomes even more useful is if you want to modify attributes of this object.
[21:49] For example, let's say that I have a read-only name, so my name cannot be changed, it's read-only. If I hover over this name property here, or actually just try to change it, I'll say a.name is equal to something, you can see I get an error because this is a read-only name property.
[22:03] Well, I can actually change that particular property. So what's coming here, first of all I'll say TP, so at least these go back to what they were before. So you can see I have a string, which is a name, and I have a number. Nothing has changed about my object. But I want to remove the read-only property from it.
[22:16] What I can do is I can put read-only in front of here with a minus sign in front of it, and that removes the read-only property from anything inside my object. Now you can see I can rename my main property down here just fine with no problems. I'm able to remove read-only, or if I want I can add read-only, which means now my age,
[22:32] I come down here and try to reset that you can see I also get an error because now that is a read-only property as well So I have the ability to add or remove the read-only property as well as the ability to add or remove the optional property
[22:44] So essentially making this optional so if I put a question mark here now these two properties are optional You can see I can leave them off and my code works just fine But I can also put a minus sign here and that removes the ability for a property to be optional
[22:57] So for example, if my age is optional up here, it's now no longer optional because I've removed the ability for it to be optional, so I need to pass in an age, otherwise I'm going to get an error with my code. Now let's just bring this back to what we kind of had before, we had string and number,
[23:11] get rid of all this extra optionality stuff, so essentially we have just a normal object right now. And what I want to do is I want to rename my keys. So essentially instead of just being a name that returns a string, I want this to be a function called gitName that returns to me a string when you call this function.
[23:23] Well, that's actually something that's quite easy to do by using the as syntax. And this is something you're probably used to with renaming or redefining what a type is. You're saying this thing is forced to be this particular type. But the as syntax in this particular case allows me to rename what my key is.
[23:38] So by default, the key is just going to be the same as what it was before, but I'm able to rename what that key is going to be. So I can rename that key to anything that I want to name it to. So I have my P property here, that's what my key was originally called, and I can just
[23:50] put the word git in front of it, and now inside of here, I need to put git h and git name. You'll notice that it's not the greatest format. I wish the a here and the n here were capitalized, so I can actually use the capitalized default function directly inside of TypeScript to now
[24:05] capitalize whatever that p type is. Now I'm getting an error because essentially an object can by default have keys that are going to be a string, a number, or a symbol. In my case, I want this to only be string-based, so I can say that this is going to extend a record,
[24:18] which is going to be string and any and that's essentially just saying that my key here this P must be a string. Now I'm still getting an error because TypeScript's not quite smart enough to know that but I can just ampersand this with the string
[24:31] type and then essentially coerces into being a string of some form. Essentially saying it must also contain all the properties of string. So now if I hover over this A come down here you can see I get get a and get name but right now they
[24:43] still return a string or a number they don't actually call a function. So instead I I want to turn these into a function by just doing this. So now they are actually functions instead. So now, getAge here is a function that's going to return a number and getName, just like that.
[25:00] This is also going to be a function that returns to me a string. And you can see now, essentially what I've done is I've taken this particular object and I've created Gitters for all of the different properties inside of that object as a brand new type.
[25:12] This isn't something that I use all that often, but the ability to essentially map one type to another type I think is really powerful, because objects are something you use all the time inside of TypeScript and JavaScript, and it often cases where you need to do this type of mapping,
[25:25] and it's in key of in combination with as is the only way to really efficiently do that. Now, if you enjoyed this video and you're looking to take your TypeScript skills to the next level, I highly recommend checking out my TypeScript Simplified course. It'll take you from absolutely beginner, all the way to expert-level TypeScript developer.
[25:40] I'll link that down in the description for you. And if you want to see a more in-depth dive into the code that I showed you to flood this video, I'll link that video right over here, where I go over all of the code and sender in the Internationalization File Library. With that said, thank you very much for watching, and have a good day.