Check out a free preview of the full Intermediate TypeScript, v2 course

The "satisfies" Lesson is part of the full, Intermediate TypeScript, v2 course featured in this preview video. Here's what you'd learn in this lesson:

Mike demonstrates how to use "satisfies" to retain the most specific type while still satisfying a given interface. The use of the "extends" keyword in TypeScript and how it is used to specify subtyping relationships between types is also discussed in this segment.


Transcript from the "satisfies" Lesson

>> So we've got a question about the use of satisfies, which is a little bit, it's not super related to this topic, but I can show you how satisfies would work in an example. So let's say we had an interface ColorWithId and it extends HasId, and optionally, it has a color.

This describes the type of what we've been working with here. And let me grab one of these. Cool, so this type checks as it should, right? It's got the Id, that HasId wants us to have up here and it has the color from ColorWithId. Here's the problem. Good, man this code is trying to help me too much today.

I can't do this, color is possibly undefined, it's possibly undefined because of ColorWithId, right? It is possibly undefined if we regard my color as this type, that's just the reality. Now, we could do this and the error would go away, look at myColor. But now we've lost the guarantee that this object satisfies the type, ColorWithId, right?

We could do this. There's nothing here that's gonna make sure that we include brightness. So this is now a value that at one moment happens to satisfy the type, but they're not related. There's nothing in our code right now that type checks for that. So the satisfies keyword lets us do this.

We get the best of both worlds. So what we're saying here is, if we look at myColor, look at that type. Color is there for sure, that's why I can call substring on it. And here, we are type-checking against the ColorWithId interface. Like, if I were to delete id or rename this property, we're gonna get a type error saying, you gotta give us an id, that's necessary to conform to this type here.

So what what's happening here is we're kinda getting the benefit of an explicit type annotation. But we're also retaining the most specific type that we can retain, right? That colour is a string, it's not string or undefined, it's a string.
>> How does that different from as?
>> Good question, as it's casting,we could do that, it would get you some things here.

What if we did this Yeah, this is interesting. So as is sort of forcibly regarding a type as this other type. So I'll tell you one thing, like you do get a similar kind of type checking here with casting in that this has to be a subtype of ColorWithId.

But look what happened below, like look at what we ended up with here. We still end up with ColorWithId, I would call this using this as keyword oops. It's very, very similar to this, almost identical. And the end result is color is undefined. You're saying here's my value, regard it as this other thing.

We do not preserve the most specific possible type. We preserve exactly what we said should be preserved, ColorWithId, represented as that thing. Regardless of whether we know that there's something, we could even do this, if we did color Right, so here we could say, this is green or blue or red.

Right, but we assigned it green, if we did satisfies, If I could spell satisfies. Just green. So it certainly conforms to the type, but we're retaining the most specific thing we could retain. It is a ColorWithId, but even more specialized.
>> Does that update then when you mutate the variable type?

>> Can you put an update them in?
>> So like, right now it's specified to just be green because your first check on the interface and then you check them later on being like, oay, so this is the most specific later on if you update to blue per se well then

>> Where would you like me to update it right here? Like, after your substring line, like say you update myColor.color to blue, will the type still. That's what I was curious about. So once, this goes back to I began the fundamentals course by pointing out that variables are born with their types.

So once we create this, once it's green, that's green, inherently, we cannot give it a blue. And satisfies, sometimes we've done this. And that might do a similar thing here, right? We're just saying, this is the way I want it to be, it's a very specific thing. Make the most specific possible assumptions you can make when figuring out what the types are.

So it's similar in that respect. It's gonna kind of be, it's gonna be very specific, but that's what you, that's what you are doing when you're saying satisfies you're saying, type check it against this thing, but retain the most specific type you can possibly retain. And it's almost the TypeScript is gonna, like shrink wrap that thing, whatever it is, it's pretty rigid at that point.

But that's a feature here, that's part of what you are aiming to do with satisfies. And if you didn't want that, you would just say, ColorWithId. Now you have the flexibility, you don't need to use that as an interesting little side trip there, worth it for sure. So I want to just, before we move on Just going back to this typerem quickly just want to talk about this word extents.

And this is used in more than one place right like this is I've referred to this as a heritage clause right when you use it for inheritance. Like class, dog extends base class, animal. And I'm gonna make the argument that they actually mean the same thing. What you're saying is, one thing is a subtype of whatever it's extending.

A dog is a subtype of an animal, all dogs are animals. Anyone wanna disagree with that statement? Good. Here, we're saying T is a subtype of HasId, it might be exactly HasID, but it could be something more specific. It could be HasId, but also has a color on it.

So that may help help this click, there's another place where we're gonna see this and that's conditional types. And it means the same thing there as well. So if we think back to our Mental model of types representing sets of values, that means all the possible things that T could be must be included in all the possible objects that have an ID property.

It's a way of specifying that requirement, T is a subset of HasId it's a subtype of HasId.

Learn Straight from the Experts Who Shape the Modern Web

  • In-depth Courses
  • Industry Leading Experts
  • Learning Paths
  • Live Interactive Workshops
Get Unlimited Access Now