Check out a free preview of the full TypeScript 3 Fundamentals, v2 course:
The "Use Cases" Lesson is part of the full, TypeScript 3 Fundamentals, v2 course featured in this preview video. Here's what you'd learn in this lesson:

Mike warns against using generics innapropriately, and gives an example of a bad use for generics, then answers student questions.

Get Unlimited Access Now

Transcript from the "Use Cases" Lesson

[00:00:00]
>> Mike North: So the last thing we're gonna tackle here is when to use generics. It's a really, really powerful idea. But people who run into trouble with types that begin to explode in complexity, often that comes from overzealous use of generics. And I would also be careful reading Blog articles.

[00:00:23] And question the information that you see out there. Because this is one thing that a lot of content gets wrong.
>> Mike North: I wanna show you a bad example here.
>> Mike North: So we've got an interface called shape. It has a method on it called draw. Circle extends from shape, circle has a radius.

[00:00:47] So here, we're saying draw shapes. And we have a type parameter where S must extend shape. And we take in an array of S's, and we iterate over each of those and draw. Here is another implementation that just uses a base class here. So generics are great for relating two things.

[00:01:11] I take in an array of t, and I give you back a dictionary of t. The generic, that type parameter, is what ties those two together. You give me strings, I give you back strings, right? Here, we're only using this type parameter once. And you can always eliminate a type parameter that's used once.

[00:01:35] There's a convention that people like to use where, I don't disagree with it as much. Where they'll, for convenience, use a generic for a type assertion. For example, they'll wrap fetch in a function and allow you to pass a type parameter indicating the structure of the response. Okay, I mean, you could certainly eliminate it, right?

[00:02:02] But there is some convenience factor there. This kind of thing that you're looking at in front of you, there's no convenience here. This is just unnecessary abstraction. But what this does show us, it is useful for one thing. It does show us that specifying this type here, that's what allows us to access this method draw.

[00:02:23] This gets us the same thing as this. So constraints on type parameters are equivalent to specifying the type on an argument, in that it dictates what you can do within the function on s, right? In both cases, we've got dot draw, dot draw, right? Up here, it's coming from this.

[00:02:46] Down here, it's coming from that.
>> Mike North: Yes?
>> Speaker 2: So on line 121, if shape was not an interface and it was a type alias, would it not work?
>> Mike North: It will be fine, I think. Type shape equals this. Yep, no problem. This is the, we're in the very large amount of overlap where there's no real downside to choosing one of the other.

[00:03:18]
>> Speaker 2: Sure, well, I guess I'm just confused on why on line 121, if you're passing in shapes and they have radius, wouldn't that error? Because it's just expecting something with only draw.
>> Mike North: Can you articulate why you would expect it to error so I can answer that question?

[00:03:39]
>> Speaker 2: Well, yeah, so because the interface, something implements an interface, but it could have other attributes.
>> Mike North: Totally.
>> Speaker 2: With a type alias, isn't that like a strict definition? It has to have this stuff, and if you pass a circle in to draw shapes to, and you're only telling it I want a shape, wouldn't it also expect a radius?

[00:04:05]
>> Mike North: If I was demanding circle here, yeah, I would say this is an overly
>> Mike North: This is asking for too much, in that I don't need radius down here. I have access to radius now. If I needed this, I would demand circles. I would say you've gotta pass me circles.

[00:04:28] The rule I like to follow is you ask for only what you need and you return everything you can. So you ask for only what you need so that you can use this with square and triangle and 2D point, which is just gonna have an X and Y.

[00:04:45] If that's gonna work, if it's all up to the draw function, then you need no further details. This is your minimum requirement, and you can stick with that. Now, on the other hand, if you're returning something, you want your consumers to be able to access all of the richness of this object to whatever degree is safe.

[00:05:10] So let's say we had multiple types of collections, like a list, an array, which extends list, a linked list. If array and linked list have special methods on them, there's no reason for me to kind of give you back a very low level base class. That would prevent you from safely accessing to reach functionality underneath.

[00:05:29] So ask for only what you need, return everything you can. Yes, sir?
>> Speaker 3: So the two functions are equivalent, then? But you're saying the top one is just overkill on the generic?
>> Mike North: They're equivalent, and they will compile to exactly the same thing, aside from the name of the function.

[00:05:46]
>> Speaker 3: Could you give an example where you would actually want to do the-
>> Mike North: Absolutely, how about this?
>> Mike North: Let me think for a moment. An example where the type parameter would be necessary?
>> Speaker 3: Where you'd actually want to use it on the generic.
>> Mike North: Yeah, okay, here we go.

[00:06:12]
>> Mike North: So I'd have to involve something else. Sorry, that's not true.
>> Mike North: That's Boolean. I'd have to introduce a second use of this type parameter. For example, return an array of these things. And I'd say map, and
>> Mike North: S is drawn true. Return s.
>> Mike North: So now, let's see, why is this unhappy?

[00:06:45]
>> [INAUDIBLE]
>> Mike North: Thank you.
>> Mike North: Okay, so now, the fact that I'm using this in two places, this serves a purpose. Without this, even if I just were to say it return shape, we have a significant downside. In that, if I were to try to say drawShapes1 and pass an array of circles.

[00:07:19]
>> Mike North: There is my circle. What don't you like there? Radius is a number.
>> Mike North: I'd have to be explicit about this. It already is saying this is an unknown property on shape. I could create this outside.
>> Mike North: And now, it'll be happy. But now, I'm in a situation where if I wanted to chain off of that, I have reduced this down to an array of shapes.

[00:07:56] I don't have an array of circles anymore. I didn't get out what I passed in, I got out the lowest common thing that the function needed. Whereas if we return an array of s's, now radius is present.
>> Speaker 3: If we pass in circles, you're gonna get circles back.

[00:08:15] We pass in squares, we get the squares back.
>> Mike North: And you can see it right in the tool tip. See how all of the s's have changed to circle? The blank has been filled in at this call site. And this is where type parameter would get you something.

[00:08:29] In that drawShapes does not care about the particulars of the shape. But what it will do is give you back what you passed in.
>> Mike North: Or I could return a promise that resolves to s or something. But the key is, I've introduced a second use of the type parameter.

[00:08:49] And that makes this generic worthwhile. Now, the type parameter is describing a relationship between arguments and return. And for like classes, you could say I take in this in my constructor, and that method returns this type. There are many more opportunities with this classes because you have so many different parts to them, right?

[00:09:12] But you gotta be able to point to two. If you can't do that, you may just be using a type parameter as sort of an assertion, right? Or you're saying give this back to me as a string. Give this back to me as an array. That's not the same thing.

[00:09:29] That's not what type parameters are typically used for.