TypeScript 5+ Fundamentals, v4

Structural vs Nominal Typing

TypeScript 5+ Fundamentals, v4

Check out a free preview of the full TypeScript 5+ Fundamentals, v4 course

The "Structural vs Nominal Typing" Lesson is part of the full, TypeScript 5+ Fundamentals, v4 course featured in this preview video. Here's what you'd learn in this lesson:

Mike explains how type checking occurs when assigning values to variables, passing arguments to functions, and returning values from functions. Concepts such as type equivalence, static vs dynamic types, strong vs weak types, and nominal vs structural type systems are also demonstrated in this segment.


Transcript from the "Structural vs Nominal Typing" Lesson

>> First, let's talk a little bit about what type checking is. And type checking happens whenever we're trying to assign a value to a variable, when we're passing an argument to a function. A function returning a value, right, remember our number return type, and we were playing with undefined there.

It's seeing what you're actually returning, how does that compare to what you've stated you'll return? So that's another place where TypeScript is asking the question of type equivalence, is type y equivalent to type x? And it's really, this question comes down to like, does the type of y fit within the type of x?

So if you're returning a number from a function, if that's what you say you're going to return, and you return 43, always, for 43 fits within set of numbers, that's perfectly fine. But if you say you're going to return the number 43 and you return some other random number, that doesn't fit.

It's really, if we think about our mental model of sets, it's a question of subsets. Is one type a subset of the other type? So, in this case, this is very similar to what we had in our add function, right? We flipped a coin, math that random, see if it's greater than 0.5, and we said we would return an array of strings, but we're returning undefined.

We're returning a value of type undefined. And that is nowhere in the set of string arrays, the set of all possible string arrays that could exist, undefined is not in there. And so, we would say, well, our return value here, it is not type equivalent to string array.

Because that value undefined cannot be found in the set of all possible string arrays that we could ever create. So, this question of type equivalence or if we think about type systems, we can sort them in a couple different ways. First, we have static versus dynamic. So, dynamic types, you can think of those as like the way JavaScript works, sometimes people call this duck typing.

And that means we try to proceed as far as we can, if some function is available and that's all I need, I will use that. If it quacks like a duck, I will consider it to be a duck, that's what duck typing means. That's dynamic typing, we try to do the best we can with values at runtime, and JavaScript behaves that way.

Static types are types that are defined in your code. They're an analyzable at build time, they're explicit. And that's part of what TypeScript adds to JavaScript, right? We're writing in colon string. We're saying this function returns a number. That's those are static types. Static meaning they don't change over the duration of the program, that's kind of how to think about the word static here.

So other common programming languages are Java, C#, and C++, they all involve static types. Many other languages do as well, but these are really, really popular ones. And dynamic typed languages include Python, Ruby, Perl, and PHP, although, there are sorbet types for Ruby and I think, Python has TypeShed.

There are ways to add some level of typing to these languages, but at their core, they are dynamically typed languages. Duck typing is very similar to dynamic typing, and we'll talk a little bit more about that in a second. So, you may see the term strong versus weak types, this really strong and it's not really a spectrum that makes sense for type systems.

Although people treat the static types as strong because they're more explicit. They usually allow you to articulate more interesting things, but there is no consensus definition of what strong means or weak. But these terms are test around a lot. All right, this is kind of the interesting part.

What is a nominal type system versus a structural one? So this is a piece of Java code. And you'll notice it's Java code because we've got like this public word before the class and public static string. So, we've got our car here with make, model, and year. And we've got another class here that is a car checker.

And it checks a car and it returns a string. This is where you put the return type in Java. And we could create a car. And if we do CarChecker.checkcar, we don't see the implementation of this function. But if this were a type equivalence check, what Java's really gonna be asking is, are you an instance of the class called car?

It doesn't matter what properties you have, you have to be an instance of a class named car. This class, that's what nominal means when we say a nominal type system. All that matters is your constructor. Where did you come from? Typescripts type system is structural. So here's an example of, we have a class car, we have a class truck, we have an object we've created, which has some inference, right?

It's gotta make model in here, we've been dealing this all morning. And when we've got our printCar function, it's happy to take the car, the truck, the vehicle, all that matters is that we meet the requirements of what the argument wants. We don't have to have come from a particular constructor.

We just have to have the same structure. We have to have the right properties, that's basically the right shape. It's not just properties, right? Cuz we could need a method to be present on the argument passed to us. So, I mean, it's still a property, it values function, but we could require that an index signature be present.

It's just a comparison of these two structures without regard for where that object came from, it doesn't matter, like car and truck, there's no parent class here they're not related. They just happen to have fields that meet the requirement, this is a structural type system. It is incredibly flexible, and it needs to be, because part of the goal of TypeScript is to layer types on top of JavaScript.

And there's a whole lot of JavaScript code out there that needs a great deal of flexibility in terms of articulating how the types work. This is what lets you type just about anything, anything that you can do in JavaScript is pretty much type, like you can write types for it.

I'm sure someone can create an example where it's impossible, but 99.9% So, here's an example, just going back to our set theory example, or sorry, let me not toss around like complicated terms like that. Thinking of types as representing a set of allowed values. If we think about the print car argument, here's the set.

All values which contain a make property, which is a string, a model property, which is a string, a year property, which is a number, that's the set. Anything you can create, any possible thing that you could make in the JavaScript language that conforms to this definition, printCar is more than happy to accept.

So because Car and Truck instances and cars set, if you looked at the type car, it's like all possible Car instances, all possible Truck instances. They're each subsets of this setup here, every single one of those instances will meet this condition. So let's go back to the big question of type equivalence, is y type equivalent to type x?

It's really, does the type of y fit within the type of x? Or is the set represented by y? A subset of the set represented by x. Caitlin asks a good question here. What if the object has more properties? So I'll go back up to the example here.

What if the object has more properties than are needed? Will it still work? It'll absolutely still work, here on truck we've got towing capacity on electric, sorry, on car we've got is electric. Those sort of paths straight through, the function is more than happy to work. This kind of relates a little bit to the access property checking, where we had that color red and remember that was like an inaccessible property and TypeScript was warning us about.

But the function is perfectly happy to take anything that meets this condition. This set of allowed values, you could have like an object with a thousand properties that the function doesn't care about. But as long as it has make, model, and year of the right types, printCar is totally happy to receive that.

So what this lets you do is, instead of having to have this big ontology of different objects, right, these modeling everything in your program as instances of a class, and you can just say, all right, here are the requirements I have. I'm building this print car function, maybe you start with this line and you're like, I wanna print this.

What do I need to be true in order to safely print this? Well, I just need to have a variable, which has make model and year on it. Willis asks, what if you use constructor in the car or truck? would print car still work? I'm just gonna point out these two lines here, I think that's what you're asking.

So an instance of a car instance of a truck works just fine. Okay, Samuel asks a good question here, I think he's just saying structural typing is different than any language you've worked with. So what does typeof really mean? Is it just the name of the class? Typeof in TypeScript means the same thing it does in JavaScript, that is the Typeof you're used to using.

Instanceof might be what you're you're looking for. Like, if, Something like that without this extra if. This is how you would sort of determine whether something's an instance of date, but it's important to know what you're doing here. It's a little bit like nominal types, so if you think about it, you can define a nominal type system within a structural type system.

Structural-type systems are, they include everything that you can do in nominal type systems. Like this, right, here we're not saying, does x include a specific set of properties on date. We're saying, did this come from the date constructor? So this is a very nominal-ish thing to do, but that's okay.

Structural-type systems are about allowing that and a greater set of capabilities, such as the flexibility of just stating, give me a thing that has these fields on it, I don't care where it came from.

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