TypeScript Fundamentals, v3 Structural vs Nominal Types
Transcript from the "Structural vs Nominal Types" Lesson
>> So we're gonna take a little bit of a step back here. So far we've talked about variables, simple function argument types, and return types. We've talked about objects, arrays, and tuples. And now I want us to step back and consider different kinds of type systems. And more specifically, how do we categorize different kinds of types systems?
[00:00:23] Because we hear a lot of words to describe these things, right? Static, dynamic, duck typing, some people use this term strong types. What does it all mean? And what is the way to describe what we're seeing type scripts do? So first, we need to understand, what is type checking?
[00:00:43] Type checking has to do with answering a question about type equivalence. As in, when we're using a foo, a function here, is the value we're passing in, equivalent to what the function is designed to accept? It's a comparison, right? Does this align with the constraints of where I'm trying to use this value?
[00:01:11] That is type compatibility or type equivalence. That's the question that we're evaluating when we analyze this code, and think about whether we should have an error on this line here. This happens when you pass a value into a function. This happens when you assign things. So in this case, let's say x and y are both variables.
[00:01:34] And here we'd be wondering is the value that y holds type-equivalent to what x wants to hold? And if so, this assignment may proceed without objection. But if x wants a number and y hold the string, problem. Type equivalence checks happen on return types, especially when you have this explicit return type here.
[00:02:00] Is my strings an array of strings? That's what we said we would return. Are we returning it? Is the value I'm returning equivalent to the type this function says it's going to return? So this is the same question, it's just sort of asked in several places. You can check this footnote out if you wanna see another example that's weird.
[00:02:27] But this is most of what you wanna think about. So, with that question in mind, let's think about different kinds of type systems and how they answer this question. So the first way to categorize different types systems is static versus dynamic. Static type systems have you write types in your code.
[00:02:54] And that's TypeScript is one of these things, right? Java, c++, C sharp, any of these things are static type systems. It could be argued that Python with type hints also kind of fits into this category, but there's not much enforcement there. So it'd be a hard thing to defend.
[00:03:16] But TypeScript is definitely a static type system. Types are in your code and they're evaluated as part of the belt. Dynamic type systems perform their type equivalence check at runtime. So, this includes a bunch of languages here, where typically, you're just gonna see variable declarations that just say, I have a variable name and its value is Mike.
[00:03:43] And there are no types there, right? There's no support really for adding types in a formalized way. We can also think about type system as either nominal or structural. This is a weird one, because TypeScript is a structural type system. But just about every other thing you see in the programming language world, they are nominal type systems.
[00:04:09] So nominal type systems is all about a name. That's what nominal means. So here's a sample of Java code. And you can see here, similar to TypeScript, we have some properties, they are associated with some types. We have a class that contains these properties. And we have a function here, print car and it takes a car as an argument.
[00:05:35] So here we have similar other classes here, we can even have Car and Truck which kind of share some properties. We've got make model and year, they exist both on Car and Truck. And we've even got an object here, it's not an instance of a class, it has make, model, and year.
[00:05:58] Turns out, everything's gonna work with printCar. All printCar cares about here is do you have make, model, and year? It doesn't matter if you're an instance of a class, or you're just a regular object, or maybe you're some weird function with some properties hanging off of it. I mean, that's a class, right?
[00:06:19] But [LAUGH] it'll work with any of them. All it cares about is look, I intend to reach into whatever you give me and I'm gonna use make, I'm gonna use a model, I'm gonna use year. I don't care about the other stuff going on around it, here's what I need.
[00:06:38] That's a structural type system. And these words here, Car and Truck, they're just around for our convenience. They're around so that we as code authors can make sense of what our code is doing. They play no role in any sort of type-equivalents checking. You could make something that might simulate what's happening in Java.
[00:07:06] You'd have to say give me a value that has a constructor property, which is a function that returns cars. Okay, you've defined the structure of something that is a factory of cars and that is the car class. Okay, you could do that but normal TypeScript code will not, right?
[00:08:21] In that, you just care about having what you need, right? You don't care about who created it. It's similar to structural typing in that way, but it's also dynamic typing, in that there is no type information at build time to evaluate. So all of these errors are popping up at runtime.
[00:08:39] Does that make sense? I mean, it might be a little bit of a stretch to say it's similar to structural typing. But what they do have in common is they have no concern for whether you came out of a class or whether you're an object literal. As long as you have what is needed in order to carry out the operation, we'll try to do that.
[00:09:01] But structural typing usually goes with static types as well, right, where you have that type information. Okay, finally, we have strong versus weak types. These terms don't really have good definitions. There's no such thing I'm aware of that's called strong typing. When people use the word strong typing, I think they usually mean, static types, types that are in your code.
[00:09:27] And when they say weak types, I think they mean dynamic types. But there is no consensus around a definition for any of these terms. So you won't hear me use strong types in this course, except right now? Static and dynamic, right, are the types in the code, or do we attempt to sort of YOLO it and figure out if things are gonna work at runtime?