TypeScript Fundamentals, v3 Classes & Access Modifier Keywords
Transcript from the "Classes & Access Modifier Keywords" Lesson
[00:01:04] We're very used to knowing upfront, like what's gonna be here, these are the things that I can expect on this interface. And that's not true if we can write arbitrary logic in the constructor and just start tacking things on. As a result, if we look at the way TypeScript analysing this, we can see that yes, it knows that this is an instance of a car, but it's not gonna provide us any kinda type safety around which methods we can invoke.
[00:01:30] And furthermore, all the arguments, the constructor arguments end up being any as well. So we need to add type information to this situation and get into a world that's providing us with that useful feedback we have seen so far in the course, where the wrong types of arguments will start to present error messages.
[00:01:50] So this is what the equivalent code would look like in the TypeScript world. And I want you to notice a few things. First, we state the possible class fields and their types up front. Second, obviously, just like a function call signature, we have types for all the arguments passed to the constructor.
[00:02:15] And then this code remains unaltered, where we sorta take everything the constructor received and we can now safely tack it onto the interface, or sorry, on to the instance of the class. We are now rightly stopped when we try to invoke a method that doesn't exist. And we're rightly stopped when we attempt to, like pass arguments to the constructor in the wrong order or just otherwise fail to meet the demands of what this wants to receive.
[00:02:45] On top of this,TypeScript adds access modifier keywords. And here what I'm saying is, I just want to acknowledge this code is getting very verbose. Like the word make is here, here, here and here. So it's like four times for each property. We're working our way up towards a simpler way to write this.
[00:03:07] We need to talk about access modifier keywords first. So access modifier keywords, you can find these in Java and C#, they all mean the same thing broadly across a wide range of programming languages. Public means everyone can see something. Protected means a class can see something and also subclasses can see it and then private means they're only visible to the class itself.
[00:03:37] And when I talk about something can be seen, I mean class fields or methods. So we use these words to indicate the visibility of class fields and methods. So, here you could see, all right, we're exposing these class fields to the outside world. Maybe we have a VinNumber that the car class can see and any subclass of the car can also see.
[00:04:02] But maybe there's this door lock code that we can unlock all the doors of the car. We wanna keep that private to the car, not even subclasses can see that. So I want you to see a couple things here. First, this pattern, right? So, doorLockCode is private. But you can certainly provide, you can sorta open up access to subclasses by defining a method or sometimes people define like a getter and a setter property, it's not just about keeping things hidden so that people don't peek at these values.
[00:04:42] I mean, if you're running your app in a browser, you can't keep secrets there coz you're sending all your code to your user. But you can control the pathways that people will take in order to get certain things done. So if you wanted to unlock all the doors and use your door lock code in a specific way and not expose that directly, you could do that.
[00:05:06] And you could say this is a private thing, but I allow it to be used in some form through protected method. Let's look at a subclass here. So sedan extends car, sit in can see a VIN number just fine. That's protected. It cannot see this door lock code because it's private and it's only accessible within the class car.
[00:05:36] And here, we're kinda applying the same pattern again. So we have a public function that's called unlock, which calls this protected method unlock all doors. So you see we can use this way to control visibility into what's available on instances of this class as a mechanism of providing a nice surface for people to interact with, where they're not able to get a directly modify state.
[00:06:08] And this might lead us if we have a different locking mechanism on this car, we can now safely change that up here, right? Nobody can possibly see this. So we can feel confident that if we change it, we're not going to break anybody, as long as this method still does what it's supposed to do.
[00:06:28] So it's a great encapsulation tool. A lot easier than just sorta overloading your constructor and using that scope as private, which is another common pattern. And obviously, from the outside of this class, so here Here's our sidin class. This is the ending brace of our class declaration. And here we're creating a sedan, we can see this public thing, but we can't see this protected thing, and we can't see this private thing, right?
[00:07:06] So it's saying this is protected, and it's only accessible within the car and its subclasses So that's private, public and protected. And this limited exposure concept is really what I just described. The ability to take something that is hidden and provide controlled access to it. Through, you know something that is more visible, like exposing a private thing through a protected method, something like that.
[00:08:37] These work really nicely in TypeScript. This is private at runtime. They're a little more difficult to access. But again, I would not advise you to put encryption stuff here. There's one last thing that's kind of like an access modifier keyword. I say kind of because it can be used in combination with other keywords.
[00:08:59] It's called read only. And this will yell at you if you attempt to reassign. Values. It's not about mutability, it's about reassign ability. So think of it more like const and less like a frozen array of some sort. It's really about not pointing year to a new number in this case.
[00:09:28] Keep in mind, giving it an initial value in a constructor is totally fine. After it receives its initial value, that's when it starts monitoring, and watching, and making sure that you don't assign it to something else. Great, we've made it through access modifier keywords. Now let's talk about puram properties.
[00:09:52] The more concise way to write classes in a way that doesn't involve the word make 1234 times each property we have, that will become a class fields four times we have to write that word So this is the abbreviated, well, this is not the abbreviated syntax, but this is the before, and here's the after.
[00:10:20] Put them both on the screen at the same time. So it looks kinda strange, but the key thing is we have an access modifier keyword. Before our constructor arguments. And it means exactly the same thing. The code on the bottom is going to compile to exactly the same thing as the code on the top.
[00:11:06] And in my constructor, please store what I received. In my class field puram properties to constructor parameter, and it becomes a class property. And this is the only time you'll see an access modifier keyword like this next to something other than a method or a class field So, the most important thing I can tell you, this is like a common snag people hit is we have to study what happens around constructor logic.
[00:11:47] Class field initialization, these puram properties, right? There's a lot of stuff that's sort of going on as we instantiate things. So I've created for us an interesting situation here. Where we have a class field with an initializer oops. And don't don't worry about console log like not returning anything.
[00:12:14] We just want to see when the console log happens. Right? We have a super call. We have one of these which sort of that involves an assignment to a class field. So understanding the order of operations here is really important. Right? When is this going to get invoked?
[00:12:34] What happens before what? Well, when we compile it out, we can kind of see. And thankfully, I've written. Class field initializer here, custom constructor stuff. So we should hopefully be able to recognize this when we see it in the compiled code. So, let's look at this. So first what happens is super, then we have these puram properties, followed by class field initializers.
[00:13:01] And only then does our constructor code actually run. That's right here, right? This is the last thing to run. So that's the order of operations. Prim properties, other class fields, and then our custom constructor stuff. And before everything, the absolute first thing, Is the super call. When in doubt, write a small example pass it through the TypeScript playground.
[00:13:30] That's all I basically did here. So that you can see that this happens, right? So, when in doubt, make a small example and kick the tires. And although it's possible to put stuff before super. You can't combine it with these parameter properties, or class field initializers. Once you have those super must be the absolute first thing in your class or in your constructor So I can prove that to you.
[00:14:10] If I take this away, And that now we're fine. So it's it's these things this automatic constructor behavior that Makes it so that pre super logic is forbidden.