
Lesson Description
The "Generics" Lesson is part of the full, TypeScript: From First Steps to Professional course featured in this preview video. Here's what you'd learn in this lesson:
Anjana explains generic types in TypeScript, where a type can take a type parameter inside angle brackets, allowing for flexibility in defining types that can later be instantiated by other types. She demonstrates creating a reusable type to handle scenarios where a type may be null or of any other type.
Transcript from the "Generics" Lesson
[00:00:00]
>> Anjana Vakil: And this is an example of a generic type So a type like Promise, it's defined as a generic type where it takes in a type parameter inside those angle brackets And in the declaration of the type Promise, we don't know what's going to be inside those angle brackets It's a parameter, like a function parameter, sort of So in a generic type, we pass in sort of like a type variable, which is going to then later be instantiated by some other type
[00:00:43]
And the convention is to use like a capital letter, usually T, and then U and then whatever annoying things, but you can name this whatever you want So here we have a different generic type That's one I made up, but again, arguably a good idea So let's say we want to be able to have a reusable type where we can say, "Hey, this thing, whatever type it might be, it may also be null." And we could go through and we could make type unions
[00:01:19]
Like if I want type nullable number equals number or null, type nullable string equals string or null, I could do that But that works for if I have a couple of different options If we really have no idea, it could be anything like a Promise, or we really wanted to be able to take in anything It could be that user type or an event or some other random type that you define and I don't know about
[00:01:42]
I'm not telling you what types to nullify That's your business, not mine So we parameterize the type by essentially saying, "Hey, there's a variable here that I don't know about yet, but whatever type gets passed in between those angle brackets to my type Nullable, the ultimate type is going to be that type T or null." So, for example, and we can do this in the playground for better syntax highlighting
[00:02:19]
For example, if we have our Nullable type, it can take anything in between those brackets And we say, "Hey, this variable, I want it to be a nullish number It could be a number, it could be null." Now we already knew how to write that as number or null And that will do what we want it to do A number's fine, null is fine, string is not fine However, since we don't, if we had a whole bunch of other different values that we might be using, we wouldn't want to like retype that like number or null, string or null, blah blah blah
[00:03:09]
And also I might really not know what type somebody is going to want to nullify And so that's where making it a Nullable and passing in the string—uh, sorry, a number type as the parameter—is now going to give TypeScript that same information in a more flexible way where I can also have now a nullable string, let's say And as expected, like a string is fine, null is fine, but a number, not good
[00:03:57]
Undefined, not good And I could even let, make an object property value Now this is like a literal value here that I'm getting here, or I could say, you know, have something like my more generic, could be any string And if I do that and I now try to assign object or not to property, success I know that's fine But if I try to assign it to an object that doesn't have that property, it's missing
[00:04:50]
But if I assign null, oh my goodness, totally fine So, similar to how we saw Promise, this is sort of looking under the hood a little bit Generic types are something that you will probably see most often in types that you're using from that other people wrote Point being, we're going to see more angle brackets as we go But when you see those angle brackets, what's between them is being passed in like an argument to a generic type that has a type parameter
[00:05:23]
And that type parameter is reused in the—just like how we give function parameters names and then we use them as variables inside of our function body, we can do the same thing with type parameters Okay, so out of the box, TypeScript comes with a bunch of parameterized types that we can use to manipulate other types And these are what's called utility types
[00:05:53]
And there's a whole bunch of them, but we're going to just look at a few just to get ourselves a little bit more comfortable with these angle brackets So one of the types that's built in to TypeScript, and I'm just going to do this over here So this was our, we had our user interface, totally fine It has a username and a string, and by default, object types, like objects in JavaScript, we can reassign values to the different properties
[00:06:25]
Like objects are mutable in JavaScript So I can have like an editable user, which is just default, where I can initialize the username as like "for now," and then I can change it later to be "no more," and that's no problem But sometimes we want to make sure that nothing is getting changed, and for that we have the concept of a read-only type And a read-only type knows that even though JavaScript would allow it, we do not want the values of this type to change
[00:07:11]
So if I have a fixed variable that I want to stay the same user forever with its same properties and their same values for all of time that my code runs, I can use this generic utility type called Readonly, and in those angle brackets, pass in the type that I want to make read-only And what that does is it tells TypeScript those properties in the user, they still exist
[00:07:51]
I can still read them But I can only read them I cannot reassign to them And this is actually shorthand for, there's a readonly keyword that you can add before type And so I could type that out for all of my types And now we'll see that just by default, user is always read-only But maybe I want some users to be writable and some users not to be writable, and so we can use Readonly as a shorthand for making the whole type read-only
[00:08:22]
Does that mean you're casting it into that function, so essentially anything that's in the function consuming it is read-only Like what you just had in terms of code, the prior block would not be affected by read-only This block Yes, because I typed this variable as a user, a regular user, where these are just regular properties No readonly there But the fixed one, I wrapped, you could say, that type in the Readonly utility type
[00:08:54]
And that is telling TypeScript, "Hey, pretend I wrote readonly on all of those properties and make sure that nobody in my code reassigns this value, these properties, because that is not something that I want here." But maybe there's an option to, you know, make yourself flexible and allow yourself to change your mind about the data, and there's other cases where you really don't want to be doing that, and so Readonly is a good way to kind of add more strict typing and more complaining to the TypeScript compiler to make sure you catch any time where somebody was trying to reassign this value that again JavaScript would be perfectly fine with, but we don't want to happen because it might at runtime create errors or unexpected outputs in our code, for example
[00:09:51]
Just a quick follow-up So does that mean it'll follow that user object the entire way once it's touched this, like if the origin of the user has passed through the read-only function, that will remain read-only if we're passing this object around Here we want to make sure we're distinguishing the user from an individual value, user or editable, that I am saying has this type user
[00:10:26]
So when I say Readonly user, it actually makes a new type, which is a type called Readonly user And that is distinct from the original user type, which may or may not have read-only properties So, what we're changing is not the user type itself The user type stays the user type, and it continues to be as malleable as we want it to be However, using this utility type, I don't have to go and make an entire new interface, ReadonlyUser, where all of the properties are readonly username—why can I never type
[00:11:14]
Okay And so on and so forth So it's essentially saving me the work of having to declare and retype a whole other type when I already know like there is a transformation I can make on my existing type that will satisfy my needs in this moment And that's what these utility types do They transform types into another type So there's no modification of the original type
[00:11:42]
It's just creating a new, you could sort of think of it as like a more specialized type of user We're going to look at a couple other examples which will hopefully illustrate this utility type concept, and I'm mostly introducing this because you will run into it in code you're reading, most likely You might sometimes need these type utilities like Readonly.
Learn Straight from the Experts Who Shape the Modern Web
- In-depth Courses
- Industry Leading Experts
- Learning Paths
- Live Interactive Workshops