How to use generics in TypeScript

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
you've probably seen generics before because typescript is not the first language to have them lots of languages do but typescript I think brings some unique things to the table so let's take a look at generics in typescript in this video my one-liner on generics is that generics allow you to wrap functionality around other types and there are two main examples that you likely have used arrays and Promises at the top here you can see we've got A1 and A2 which are both arrays first is an array of strings and second we have an array of numbers now we're being explicit about the types here saying this is an array of type string and an array of type number but even if we pulled these out I'll take this out for number for example you can see that A2 still is properly inferred as a number array now arrays are kind of a special generic in that there's two ways to represent them in the type system we could do the array and then the angle brackets angle brackets are the typical generic syntax so in this case we're saying an array of strings or with an array we could also say number or whatever the type in the inside the array is and then we can put our square brackets after that now the thing to take away here though is that the array in both the case of an array of strings and an array of numbers is always the same that is you have the same interface the same functions on the array that you can operate on so for example in X and Y here you see we're popping from A1 and A2 X returns a string or undefined y returns a number or undefined and so X and Y are strongly typed with whatever type is in the array but the interface that we call pop in both cases is exactly the same we have a common set of functionality wrapping some other type inside and it doesn't really matter what you put into the array it's a envelope or a wrapper that can contain any other type the same is true about promises we've got two promises that we're creating here and again we have a string and a number inside of them in this case we're not being explicit about the types but we can see that typescript can correctly infer these so P1 is a promise of string and P2 is a promise of number we have the same interface if you will again that we can operate on so we can do a weight P1 and a weight P2 even though they're Promises of different types we can still await both of them X of course is going to be a string and Y of course is going to be a number again we can unwrap that promise and pull out the value inside it doesn't matter what the value is so these are two built-in types array and promise that you probably use pretty regularly and that both are generics but let's look at what it means to create our own generics I'm going to paste in an example here and we can walk through this so the example here is that we have jobs that we want to run and we probably want to put them on a queue and then we want to be alerted when those jobs are complete so we have this type here which is a job run right this is a single run of a type of job we can have a job that for now we're just going to say is of type any and then the job run has three different states queued running or completed and finally we have an oncomplete callback that we can register and we will know when the job is complete notice is that the Callback takes an argument job which again is a type of any so this is the way that you might make a job run type without using generics we don't know what type of job we need to run and so we're just going to set it to any and we can't really strongly type that because we don't know and now finally we can get our job run instance by enqueuing the job in a queue so we have NQ job here it takes some job again because we're trying to support any possible type of job in the system we just have to say any we're going to have some logic to cue it up there and then we'll return our new job run so we have that job instance we have the job State as queued and then we have an oncomplete hook where we could register a callback here's an instance J of our send email job and then we can enqueue it and now we have this run here which is a job run instance and finally we're going to call our oncomplete Handler and now notice that we have this job argument and the type of course is any that was kind of a lot of setup but the point here is that we have this idea of a job run we want to know when the job is complete but we want to be able to support any type of job in this case we're not using generics and so we have to fall back on the any type which is not a great type to use in typescript if I say job dot well I can't get any kind of autocomplete support here because typescript only knows that job is any it doesn't know specifically that it is a send email job so what could we do to convert job run here to something that uses a generic to allow us to have strong types throughout our system well what we can do is say job run here takes some job type J and I think about this in two ways when you have the angle brackets here with a type inside of it first you can think of it like a function when you have a function you can pass in an argument and that argument affects the outcome in this case the argument is our type J which job run takes and the outcome that is affected is that we have a job run of a particular type J we can actually change the returned type of job run by by assigning different values to J the other way to think about this is like a template right so J here is some template value that is going to be replaced dynamically when we use it with some real value in fact other languages like C plus plus and maybe other languages actually call generic types template types so that's another mental model I like when it comes to generics but now that we're taking this J argument here what can we do with that well we can say that we expect job to be of type J and then we expect the argument to our callback here to also be of type J so now if we create a job run type of a particular job we can know that the job property on the job run will be of that type and we can know that our callback will be of that type as well okay so once we make this change to job run we can now see that we actually have an error further down here and that is that generic type job run requires one type argument we need to actually assign a type argument here what value do we want to put in there well essentially we want to take whatever the type of this job right here is and so what we want to do here is add a generic parameter to our enqueue job function and the way we do that is with the same angle bracket syntax and we put it after the function name and here I'm going to add the type J now it doesn't have to be the same as the type we did before so maybe I'll make it t just to show you that it can be different so this just tells typescript that and Q job is going to use some type T somewhere in this function and the place we're going to use it is to replace this any right here this tells us that the job is going to be of type T now again it's it's kind of an n in that we could put any type in here it doesn't really matter because we're not limiting it at all we'll get to that in a second but essentially it gives us a way to hook into whatever that type is whatever the type of the argument between queue job is now we have a type that represents it and so now I can put that in our angle brackets here at the end so I'll put my T in there all right and now finally let's replace this last any down here with a t as well so now we have a function that returns a job run of t one thing you'll notice is that when I set this T in here we did not have a type error on line 15 here and that's because type job here is of type T already and so it matches as we set up here the type J that is being passed into our job run type we ensure that those are going to be the same if for example we made this something else for example T of string then we can see that this would throw an error because it says type T is not assignable to type string so that's why this knows that this is still successfully returning the correct type okay so now that we have that in place is there anything else we need to change actually there's not if we scroll down to the bottom here our oncomplete function you can see that the type of the job argument is now the send email job which is exactly the type of job that we passed into in queue job if we were to try to use some autocomplete on job here you can see that I have recipient email and subject accessible to me and that these can now be strongly typed and so now we success fully have a generic type that allows us to manage different types of jobs and it gives us a wrapper for managing those jobs that doesn't really care about what the job is but we can use the same enqueuing functionality and uncomplete callback and we get strong typing for the job within our wrapper now we can go a step further with this because here's something else that could be happening let's say we've just passed in a simple string here to enqueue job like this right now there's nothing that prevents us from doing this and of course we no longer have a subject on that so I'll delete that but now you can see that our job parameter here is just a string and maybe that's not so useful for our system maybe there's like some base functionality that we expect all jobs to have maybe for example up at the top here I can paste in a job type and this is what a basic job should have it should have a name it should have a function that we can call to start the job and it should have a state maybe that's incomplete or success or failure and so now we say that any job should have at least these properties and so for our job run to actually work it needs a job that has those properties on it so what we can do instead of just saying J inside our angle brackets here we can say that J extends job so what this is saying is that job run will now wrap any type J as long as that type extends the basic job type and this is great for something like our queue job function right because now we can say job.start to actually kick that job off in our queue now if we take a look at our enqueue job function here we do have a problem if we hover over the T here we can see that t does not satisfy the constraint of job essentially we're saying and queue job accepts some type T could be any type T though we're not saying it has to match the job type well that's pretty easily solved when we can say that t extends job as well so now our enqueue job function will only accept arguments that have a type that extends the job type so now if we come down to our enqueue job function here we can see that it does not accept a simple string that is not good enough because it doesn't have the capabilities of a job so let's replace it with our send email job J unfortunately that doesn't have what we need as well we're going to modify the send email job type to say that it is the intersection of the base job and its own custom fields which is a nice way to do this I think and then I'm going to copy the properties that we have on our job here and we're going to have to add those to the job that we have created down here now we can see that this job is successfully being accepted here now the neat thing about this is now that we have some properties on our job that we know will always be there we could actually use them inside of our enqueue job function so maybe I'll replace this comment here with something like job dot start notice that if I do job dot we get some autocomplete here and we can have the name the start and the state the nice thing here is that we have basically anything that is on the job type because we know any argument that we receive here will at least fulfill the requirements of the job type and likely more but the nice thing is we don't care about what those other things are now this is a little bit different than in the way an array or a promise works right the array of the promise does not require any certain fields on the types that they accept the way we're doing with the job here but inside your own application you can use this technique to create generic wrappers for common sets of types within your own system I think if you look through some of the code bases that you work in regularly you'll probably see places where generics can be really handy like I said they're a great way to wrap common functionality around other types that you use frequently so play around with it let me know if you have any questions about how generic works I'll be happy to answer those in the comments if this was helpful I'd appreciate a like or subscribe and thanks for watching I'll see you in the next one
Info
Channel: Andrew Burgess
Views: 35,122
Rating: undefined out of 5
Keywords:
Id: t0qQSujSslQ
Channel Id: undefined
Length: 11min 45sec (705 seconds)
Published: Mon Sep 19 2022
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.