- Now the next thing I wanna talk about
is the index type that allows you to access
the type of properties of other types
that you've already created.
And this works for interfaces and types exactly the same.
So in this code we have a person which has a name
and they have a skill level
which is either gonna be beginner, intermediate, or expert.
And then down here at the very bottom,
we have a function for printing out
this person's skill level.
And this takes in a skin skill level
which can again be beginner, intermediate, or expert.
All of this code is working fine with no errors at all
but there's a bit of a problem in the code
and that we're essentially duplicating
this skill level in multiple places.
And the reason why this is a problem is let's
say we add a new skill level to our person.
For example, we have too many expert levels,
we need to distinguish them between master and expert.
Now I'm getting an error
because this skill level could be a master type
which is not assignable to this beginner,
intermediate, and expert.
Essentially I have two things that are dependent
on one another, and when I change one
I now need to go down here
and change this other one as well for it to actually work.
And that's not super ideal.
So instead I would like to couple them together
so that anytime I change this skill level
it's going to update down here as well.
There's two main ways that I could do this,
the first is I could just create a brand new type
for my skill level.
So I could just say type, skill level is equal to,
and I could just set it equal to this value right here.
There we go.
I could just move that to the very top of my code
and then I could down here say that this
is going to be a skill level
and this is going to be a skill level.
And now if I make changes up here
doesn't matter what I do one way or the other
those are two things that are linked together.
This works perfectly fine,
but let's say that I didn't want to do that.
There's another alternative,
so let's just bring our code back
to what we had before
where we didn't have this separated into another type.
There we go.
And now we're getting our error just like before.
Instead, what I can do where I'm defining this type,
I know that this specifically needs to be whatever type
the skill level of my person is.
So I can say I want to get my person type
and then I can actually put inside of here
the actual property I want to get.
So I want to get the skill level property.
So now this skill level is going to be,
as you can see when I hover beginner,
intermediate, expert or master.
And if I make a change up here
and I come back down to this skill level,
you can see it reflects perfectly
all the changes that I make because they're linked together.
So by accessing, using the index value
just like you would access inside of an object
a specific property
it works the exact same way in TypeScript.
I'm saying I wanna get the type
of the skill level property for my person type,
and that's exactly what it's getting for me.
So this is one way that you can link together
these things and it's really useful
for when you want to essentially link together a property
of a type with something else in your code
like a function argument or another variable.
Now this can even be taken a step further,
imagine we wanted to write a group by function
where we're grouping these people by skill level,
but we would end up with some object that looks something
like this where we would have a skill level,
for example, beginner, and then we would have an array
of different peoples, like people one,
person two and so on.
And then we would finally come
down here and we'd have some intermediate level
that would also have some people inside of it and so on.
How exactly would we create a type for doing this,
while still maintaining this skill level inside of here?
Well, to do that we need to use another way
that you can use the index type
and that's when you're creating an object type.
So let's create this person or we'll say people
grouped by skill level.
There we go.
And that's going to equal to whatever type we wanna create.
And we know that we want to have keys
for each one of our different skill levels.
So in order to say that we want to have multiple keys
of a specific type whether it's number string
or in our case, this skill level, what we need to
do is you can put these index brackets inside of here
and then you can put whatever you want.
For example, this is going to be my index
and if I type this as a string,
essentially what I'm saying is I want to have any
strings at all as my actual key for my object.
And then I can specify what I want the value
to be for all of those different indexes.
So any index that is a string will have a value
which is a person array just like that.
Now this isn't quite going to solve the problem we want
because it's going to allow any string at all,
so if I wanted to create something, we'll just say const A,
is going to have this specific type right here,
this is gonna be equal to an object,
we'll just get rid of this code down here.
And what I can do is I can put any string I want,
it doesn't matter, any string I want,
but the value of this string must be a people array.
So I could do an array and I could put a person
inside of here which has a name, file,
and then we need to add in a skill level,
there we go.
So if I do that
you can see that this is working fine,
but as soon as I change this to something
that's not a string it's going to give me an error.
For example, if I made this like a number,
or if I just came up here and I said, you know what?
Actually my indexes should be numbers.
Now you're seeing I'm getting error because it's saying
Hey, this must be a number, not a string.
Now if I wanted to use a skill level here,
well what I could do is I could use my person object here
and I could just use the index property a second time
to get specifically the type for that skill level.
Now this still doesn't quite get us there,
if we hover over the air, essentially it's saying
that an index signature cannot be
a literal type or a generic type
it must be a mapped object type.
So essentially it's saying,
hey I can't use this literal union right here
because then I would only have three different options.
So instead, all you need to do is replace this colon
with the word in and that'll give you the
exact same thing you're looking for.
So now my keys must be one of the skill levels.
You can see beginner, expert, intermediate,
and then it's going to work just fine.
And actually I need to use all three of them.
'Cause I'm saying I have an index for each one
of these different skill levels.
That's essentially what this is saying for me.
Now, we could also go ahead and say,
what if we wanted to group by name instead?
Well, what I could do is I could get the name property here
and instead of using in,
I would just use that colon syntax just like I did before.
And now it's saying that any string
is essentially going to be valid.
So if I just came in here
and I change this to any string and all it doesn't matter,
you can see that that's going to be valid.
And that's 'cause the type of my name up here is a string.
And this doesn't have to be used
with indexes inside of indexes.
I could just say string
like I did before and that's gonna work fine.
The whole idea behind this index property right here
is you're essentially saying,
I want to have any of the keys
that match this specific type.
For example, string have a default value
or a default type of a person array.
So now every single thing that matches
that particular key is going to be a person array.
Now the very final thing you can do
with this index type has nothing to do
with creating objects,
but instead is kind of extrapolating out
an object or an array.
So for example, let's just create an array, really simple.
We'll say const A equals 1, 2, 3.
And actually let's change these to strings.
So we'll just have some random strings
inside of here and let's do a Boolean as well.
So we hover over A,
you can see it's a string or a Boolean array,
relatively straightforward for our syntax.
And if we wanted to manually type that,
that would just be string or Boolean.
And we wanna just wrap that in parentheses
because either one of those are going to be in our array.
That's what our specific hard coded type is going to be.
Now what we wanna do is we wanna get the type
of something that we can put into our array.
So essentially from this type right here
I wanna get either string or Boolean.
So I want to essentially create a type
that is just string or Boolean,
just the things that can go into my array.
Well, let's create a type for that.
We can say type A, it doesn't really matter what we call it,
and this is going to be coming from that a variable.
So we can say type of A,
and what I wanna do, I'm gonna wrap this in parentheses,
so this all executes.
Now if I just do this
I get the exact same type as you can see here
it's going to be string or Boolean array.
Now I can use my index type
to actually extrapolate over that.
So you know before I could say like
of index zero and that's going to give me a specific value
but in my case I wanna get what the values
are going to be across all of the different indexes
of my array.
And when using arrays, you use numbers as indexes,
so I could pass in the type of number here.
And what that's going to do is it's going
to take my array here and it's going to go
to every single index that's possible
in that array and determine what could those types be.
In my case, my array has a type of string or Boolean array.
So my A type here you can see is now set
to string or Boolean.
It's essentially extrapolating over every single iteration
of what the number could give you for an array here.
Essentially if you pass a more generic type
so you don't pass like a specific string
or a specific number and you pass a more generic type
like number string or Kiev,
it's going to just extrapolate
over the entire object or array
and determine what are the things that could be returned
when you're using a number for the actual type
for the key.
I can do the exact same thing with an object,
let's create an object here and we're just gonna say
name is Kyle and age is going to be 28,
the comment in there, just like that.
And now instead of using a number index
'cause obviously there's no number index for this,
what I could do is I could say, you know,
just name and now my type A is going to be string
and if I said just age
you can see the type here is going to be number.
But if I want to get any of the values
that are possible for name, age,
or any other keys I add in there.
But what I can do is I can get the key
of this type of A, there we go.
So type of A is essentially just getting
the type of this variable.
If we actually gave it a specific type
we wouldn't need to have this type of in every single place.
I'm just saying whatever the type of A is
which in my case is an object that has a name
which is a string and an age, which is a number.
I wanna get all the keys from that.
That gives me name or age.
And then from there I'm taking that name or age
and I'm passing it along as an index.
So I'm saying give me all the possible values
for either the name or the age property.
And now if I hover over this
you can see it gives me string or number
and that's because my name and age could be either of those.
I could pass in another variable,
for example is programmer, let's say true,
and now A here is going to have that Boolean type
added onto it.
So all the different types that I'm possibly adding
to this A variable here are getting down into this A type.
So that's just another way you can use the index type.
For the most part
you're probably going to be using the index type
either inside of arrays with that number syntax
or you're gonna be using it inside
of objects with like a hard coded value, such as name.
Most of the other use cases are not super common
unless you're writing some more niche
or more complicated type script code.