TypeScript Fundamentals, v3 TypeScript Fundamentals, v3

Classes & Access Modifier Keywords

Check out a free preview of the full TypeScript Fundamentals, v3 course:
The "Classes & Access Modifier Keywords" Lesson is part of the full, TypeScript Fundamentals, v3 course featured in this preview video. Here's what you'd learn in this lesson:

Mike discusses how to define classes in TypeScript and access modifier keywords for limited exposure including public, private, and protected. JS private class fields, readonly, and param properties are also discussed in this segment.

Get Unlimited Access Now

Transcript from the "Classes & Access Modifier Keywords" Lesson

[00:00:00]
>> TypeScript takes the JavaScript concept of a class and layers, some really useful stuff on top of it. We're going to talk about access modifier keywords, private, public protected. We're gonna talk about private fields. And then I'm going to show you how properties work, which is a shorthand that will come in, t's very useful because without it, TypeScript classes can get really verbose as you define class fields that sorta originate from constructor arguments.

[00:00:38] So let's talk about class fields for a moment. In the JavaScript world, you've probably seen code like this where we take in arguments to the constructor, and we kinda tack them on to each instance of the class. Part of what we get as a result of this is like, the instance of the class isn't a well defined type, meaning we don't know necessarily what's on it.

[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:07:41] Just a warning. JavaScript is least client side JavaScript things that run in browsers. Please don't use this as a mechanism for storing like auth tokens or encryption, keys, anything like that. This is really just about encapsulation, not not security right? And furthermore, these access modifier keywords they disappear as part of the build process.

[00:08:14] So anyone who puts like a debugger in your program, they'll be able to see all of your fields there. So it's not, this is again, just fancy linter. This is not really private at runtime. JavaScript recently got the concept of private class fields. You can recognize these because their name begins with the hashtag as of TypeScript 3.8.

[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:10:42] I can prove it to you. Let's go to the TypeScript playground. Let's pop this out and look at the JavaScript that it creates So what's happening here is we're saying, I have a constructor argument, and I'm going to call make. I also have a class field of the same name.

[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.