OK enough about generic functions.
We will also use them for this cause all in the course project.
So that should be getting clearer and clearer.
The more we work with it.
The general idea hopefully is clear by now.
Already generic functions aren't everything we can use though we also got generic classes and therefore
let's see how we would create such a generic class and why we could do that why that could be useful
for us.
We could be creating a storage class here a class let's say which has a private property data which
in the end will hold an array of data and then it has a add item method which takes the item and that
should be added to data with push let's say
we have a remove item method which takes item where we could say this data splice and then we just need
to get the index of item here and splice one element.
This is how we can remove an element from an array and of course get items where return lets say a copy
of this data like that with the spread operator you learn about.
Now we can do that and we're getting a bunch of arrows here.
We're getting a bunch of arrows now for one because the named storage is a reserved name so let's name
it.
Data storage that's easy to get rid of.
Now we also get an error here because we're not saying anything about the kind of data were storing
and which type item has here.
And that's exactly where we can turn it into a generic class because you might not care about the type
of data we want to make sure it's uniform data.
So it's even just strings or just numbers or just objects but other than that I don't care about it.
So here we could turn us into a generic class by adding angle brackets after the class name and then
again t you whatever you want to use as an identifier and add is here as a generic type you know here
we can say this here is an array of type T.
So it stores data off that generic type in it.
Therefore here we add such data and we try to remove such data here and they'll for a year get items
correctly is inferred to return an array of generic types.
And now we can create different storage.
We can have our string or our text storage here by calling new data storage setting that generic type
2 type string here and then here and text storage I can call add item and if I try to add a number I
get an error because I am saying that this will be a data storage which only stores strings so I can
indeed store Macs here and maybe also store menu and then call text storage remove item Macs and then
console log text storage get items and this should all work if we save that because it's a generic type
and indeed at the end we just have menu in there so this would be one simple generic class we can create.
Now why would we build such a generic class.
Well just as I said because maybe we don't just want to store text.
I might also want to store some numbers in a different data storage so then I could create such a data
storage and setting this to number ensures that now we can only add numbers to data storage.
And of course we could also set up a storage where we allow both by using a union type.
So we got full flexibility there but we give types some extra information that makes this both a flexible
and still strongly typed class just as we headed with functions really flexible and still perfect type
support.
That's the whole idea behind generic types.
So here we can do that with the data storage class by storing strings or by storing numbers or whatever
we want.
We'll have one problem about our data storage class though let's say here I have my object storage.
Now I can create new data storage and say India I want to store objects right.
I want to store objects and data for of course we can go to object storage and add an item let's say
object where I have a name Max India.
Let's also store another object where a store menu then let's say we're doing some stuff in our code
and at a later point of time here I want to remove the item when to remove the item with name menu here
and then I want to console log object storage get items like that.
If we save that we see we get to object in there that's Max.
So that looks good.
Right now it's not really good.
Let's say here I want to remove Max now.
So now we should have manual left in the data storage hence if I reload a well it's still Max.
Now the problem is that we're working with objects here.
As you know objects and JavaScript are reference types now attached you find an additional resource
which allows you to dive into reference words as primitive values here.
I assume you know the difference because it's a core javascript thing totally unrelated to typescript
here.
The problem is the way our class is built with this logic on how data is removed and identified.
We're not really doing a good job when we work with non primitive values.
So when we work with objects or arrays because index off if we pass in an object here will not work
because technically this is a new object.
It might look like this one but it doesn't work because this technically is a brand new object in memory
and has a different address and indeed here javascript will look for the address and basically not find
it.
So what it then does is it removes the last element in the array here because it isn't the end returns
minus one index of returns minus one here.
If it doesn't find anything which means it starts at the end of the array.
That's normal javascript behavior and removes the last element of the array deal for.
That's why it worked for a menu it didn't really work it accidentally worked but why it does not work
for Max we're always removing the last element of the array for analysis we can't identify it no one
work around to at least fix that is to check if we find item so we can check if this code here if Dad
is equal to minus 1 which means we did not find it then we can return and make sure we don't accidentally
remove the wrong item but of course now we just fixed that but but we still don't have this in a state
where it would work with objects.
Well the only way how it could work with objects is if we pass in the exact same object again.
So here if I had my max object stored at a constant like this and then here I pass and Max object and
I do the same here then I'm really using the same the exact same object the exact same object in memory
and therefore for now it would work.
This would be the only way to make this work so therefore here the better idea might be to make sure
that this really only works with primitive values so we could say that T extends string no.
Maybe all the bully ends and so on.
So now we basically say data storage should only work with these types therefore objects are not allowed
anymore.
Does might be better here because we would need a more specialized data storage which then probably
only works with objects and turned and not with primitive values to fine tune our retrieval logic here
to maybe check for some I.D. in the item that should be removed.
The way it works here it really only works for primitive types so setting such a constraint is definitely
a good idea.
And with that if we saved it after commenting this out it again works of course now needless to say
hopefully that of course you could have more than one generic type here as well.
You're not limited to one type when you work with classes and you can also have methods which have their
own generic types inside of classes so you could introduce new generic types in class methods.
If you have some generic type which only is needed in a certain method and not in the entire class so
you're really flexible there you can use constraints everywhere and in general generic types are there
to make your life easier and to give you that perfect combination of full flexibility we can use any
primitive value we want here and type safety because we know perfectly well what is stored in this data
storage and what is stored in this data storage.
So this combination is the awesome thing.
Generic types give us.