Check out a free preview of the full TypeScript 5+ Fundamentals, v4 course:
The "Access Modifiers" 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 demonstrates how to use access modifier keywords like private and protected in TypeScript to control the visibility and accessibility of class properties and methods. The difference between private and protected, and how to define private and protected fields and methods in a class are also covered in this segment.

Get Unlimited Access Now

Transcript from the "Access Modifiers" Lesson

[00:00:00]
>> Let's add more stuff here. So we kind of have a problem. I don't like that I can just like edit the serial number on my car here like hang it goes to Den. SerialNumber=-123, that seems bad. We shouldn't be able to mess with serial numbers on these cars.

[00:00:20] So what we're gonna do is we're gonna make that serial number private and we're going to define a getter for it. So here we go. So these are the new lines here, okay? So what we have here and I have to delete this line in a sec, but I just want to leave it for comparison's sake.

[00:00:42] What we have here is a new property that we're saying is private. Private means only instances of this class can view this field. If I created a subclass, l would not be able to see this field. It's only this class itself. Protected means subclasses can see it too, but the outside world cannot see it.

[00:01:13] Just cause you created a car and you have a variable called my car. That doesn't give you a right to access the serial number. So if I delete this or comment it out, great, I've got this private field, at least it's a TypeScript private field still generating the serial number.

[00:01:34] And then I created this getter which will let, for example, this work, but I won't be able in subclasses to do something like, That, it's a read-only property. There's only a getter for it. So this is an example of us using access modifier key words like private and protected to sort of store information with one level of access private.

[00:02:07] And then expose it in a controlled way, with another another level of access protected. So like we can store it, no one can see it but we are no one can write to it or read to it and then we can deliberately open up a door that lets subclasses read it.

[00:02:25] But the outside public cannot sit down here, this is it's not accessible from the outside. You can create static, private, and protected fields as well. So we're gonna use this to kind of lock down the serial number generation up here. Really I'm just gonna add the static, sorry, the private keyword to both of these.

[00:03:09] And this is kind of interesting because, we were thinking about private and protected as pertaining to instances of the class. The mental model still holds true with one caveat. If you think about, like, a car as having this static side, this stuff up here, and then there are instances, private and protected still behave the way you would expect.

[00:03:37] In static areas of this class, I can access private static stuff. In this instance, I can access private instance stuff and private static stuff. That's what lets me have access to this function here. This is instance stuff that's happening here. There's no static keyword, right I could say same as in here like I do or car generate serial number, no problem.

[00:04:09] If I were to change these to protected then subclasses on their static side can access it, as well as on their instance side. So really think about it as a two-dimensional thing. There's static and instance, and then there's private and protected. Private and protected relate to, like, only this class, none of its subclasses.

[00:04:32] And then protected opens it up to subclasses. And then there's one more but it's implied like you don't need an access modifier keyword for this. You could say public like this. But everything's public by default. Unless you say otherwise.
>> Two quick questions. One, the underscore naming convention is just a naming convention, correct?

[00:05:00]
>> It is a naming convention, but I've done it for a reason. And that is, you have to remember, TypeScript melts away at build time. It checks your code and then it says, I'm done, good luck. I'm going home. So, if you were at runtime, you would still see this field here.

[00:05:20] It doesn't actually prevent other code from accessing this field. If you were to open up a JavaScript console. And you were dealing with a deployed website with this code in it, you would be able to see underscore serial number, and you'd be able to write to it as well.

[00:05:38] It's just sort of something to help keep you honest within your own code base. So, leaving an underscore there is important to give you a clue if you're ever, looking at a log file or something like that. Or you've attached a debugger to a running version of the app, and you're dealing with something in a console, like you still want to leave yourself an indication that this is not supposed to be public.

[00:06:03] But it's just kind of a statement of intention and it's sort of, the honesty policy in that it's designed to help you, don't defeat it. There are ways now of making fields actually private, which we will talk about later in this chapter that we're on right now. I see there's a question in the chat about hashtag fields.

[00:06:28] We will talk about that that's exactly where we're headed.
>> And the other question was just about the implicit getter, not needing a type, because it can be inferred from the class property, on line 23 there.
>> It's inferred from the return type here. So unfortunately, if we did this, I'm gonna follow my own advice, do that.

[00:06:58] Yeah, return types are inferred. It's a good idea to add the return type here. Okay, onwards we go. So we talked about static blocks. We talked about the access modifier keywords on member fields, which are instance fields, and on static fields. You can see now I can't just call car.generateSerialNumber from the outside anymore.

[00:07:22] That is private. Even subclasses won't be able to get that. They'll be able to access their own serial number because there's that getter, but they won't be able to generate a serial number. That happens in the base class and stays there. All right, JavaScript private fields. So the access modifier keywords like private and protected.

[00:07:48] Predate actual private fields that landed in Javascript relatively recently. In the spirit of continuing to evolve our example, let's take our serial number field and make it instead of like typescript private, which is just like the honor system. Let's make it actually private. So we're gonna go up here and I can get rid of my underscore here because it doesn't really matter.

[00:08:19] I don't need an indication of what this should be. I'm just gonna literally change its name to begin with, a hashtag. And that denotes that this is a private field. I'm getting yelled at by TypeScript because it's like, this is private. This is private. You don't need to tell me.

[00:08:38] It supersedes the honor system here. This is actually difficult to access at runtime. And here you can see we have access to hashtag serial number. But we won't be able to access it from outside the class like this. There's my car. This is not accessible outside the class.

[00:09:03] And this will match, like if you were to open up Chrome DevTools or attach a debugger to a running Node program, you would see that this is for sure private. Now, it's private in terms of encapsulation, but you should remember, if you attach a debugger, if your code is running somewhere that users have access to, they can attach a debugger and they can view this field.

[00:09:30] Don't put anything here that's actually a secret. It's not private in that sense. Chrome DevTools has an API that you can use to just grab private fields. It's not secure, it's just private. All right, we could use this for static fields as well, I'm just gonna up top and make the modification similarly.

[00:09:56] So we're gonna take the private static fields, get rid of this. And do this and we need to update this and this should be good. One more, see I should have used the like f2 to rename and it would have taken care of everything. All right so now we have legitimately like, actually private.

[00:10:27] Static fields, static methods, they cannot be accessed. Even once this compiles to JavaScript, they are still private. As opposed to this, which is just, it'll compile away and the getter will be there for all to see and all to access. It's important when you think about libraries in particular because people can just like ignore that type and just call the getter anyway and they'll get the value, right?

[00:10:55] And they'll depend on that. It's not really a security issue. It's more like they will start to depend on that thing and now, they'll break if that thing ever changes. So we talked about static fields, great. Okay, this is really cool. Private field presence checks. So we're gonna take advantage of something really cool here about private fields.

[00:11:28] So I'm gonna grab this =method. We'll drop it into the class and then we'll talk about it. Okay, so we've defined a method called equals. And the purpose of this is we want to see whether I obtain a car, I want to see if I am equal to this other car.

[00:11:53] Now, we could do it with an instance of. We could do it with checking for properties, like say, does my structure. Look like it's a car, but we're gonna take advantage of something else. We're gonna take advantage of this private field presence check. What this means is if we are given other paths to us is of type car.

[00:12:24] We're able to access it. So this is important in terms of understanding how private fields work. It's not that this instance can see that instance's private data, it's that all instances of CAR can see private data on all other instances. So here, through typeguards, we first say, all right, if this value passed to us, we can start with this, just to not throw you for a loop with unknown.

[00:12:54] If it is truthy, meaning it's not null, it's undefined, and it is an object type. Well, now we know it could have properties on it. And so what we can do then is a private field presence check where we say is serial number in other and if it is, guess what we know it's a car.

[00:13:20] The very fact that we're able to determine. So that field is there, and it's a private field, the only way we'd be able to do that is if it's another instance of the same class. So we know for sure that this is a car, otherwise we would not be able to.

[00:13:38] We wouldn't be allowed to even know if a serial number exists. You could have another class like Computer class called computer. Computers have serial numbers too but you wouldn't be able to see this you wouldn't be able to ascertain whether that serial number that you're looking for see car.#serialNumber.

[00:13:58] It's the one that you have it's the field on your class not just the name serial number, but specifically a field that you can access. The fact that you can see that means this is for sure a car. And that's why this guard here takes you from any straight to car.

[00:14:20] And now for our equality check we can actually compare serial numbers and we can see. Am I a car? Are you a car? So this teaches us a couple of things. One is that if we're given a reference to another car, we can see all of the private fields on that thing.

[00:14:38] It's not a little bubble around each instance. It's kind of above all, that all instances share. And within that bubble, they can access each other's private data as long as they have references to each other so they can reach for it. So that's how you want to think about this.

[00:14:54] And then finally, this private field presence check. We talked a little bit about type guards. This is a prime type guard. We'll talk more in the next chapter about user defined type cards where you can write your own. This is one of the most solid ones, but it is a little bit nominal type system ish, because it's legitimately checking.

[00:15:18] Are you an instance of car that's the only way you'd be able to see that private field? Dimitar, I think I answered your question just now. If another object has a serial number too, and I'm gonna assume you mean like an instance of a different class has hashtag serial number that is a different serial number.

[00:15:40] Like if we had this. Right, when you get to this check, inside this instance of Car, you're not allowed to read this, you're not allowed to know it exists. So this condition would fail. And the fact that it passes it means not only is there a private field with this name, but is it specifically the private field that you can access?

[00:16:13] And that's how we know it must be a car. Hopefully that makes sense. It's a pretty powerful idea though. It also makes private fields like much more useful because you can share data amongst each other. If you couldn't do that, it would be kind of very limited in terms of usefulness.

[00:16:37] All right, one more access modifier key word here. We've used it already. It's called read only. We can make this change really quick, but we could make that serial number a read-only field. So now it's a private field that is set when it's initialized and we cannot change it after it is set.

[00:16:57] Remember this is also a type script construct at runtime if you had misbehaving code that casts this to another type, they will be able to write to this. That's bad TypeScript code if it's like casting away read-only. So, it's just an expression of what you want this to be used for.

[00:17:21] Actually, in this case, since you can't access this from the outside world, you could be pretty confident that no one would be able to change this. You could have a method in here that changes that but you're telling yourself at that point. Yep, someone caught a bug. She'd be triple equals, and guess what?

[00:17:46] The read-only just caught that. We'll pretend I left that in there as a bug so that when I added that step, it worked out perfectly