Classes in TypeScript, like JavaScript are a special syntax for its prototypical inheritance model that is a comparable inheritance in class-based object oriented languages. Classes are just special functions added to ES6 that are meant to mimic the class
keyword from these other languages. In JavaScript, we can have class
declarations and class
expressions, because they are just functions. So like all other functions, there are function declarations and function expressions. This is the same with TypeScript. Classes serve as templates to create new objects. TypeScript extends the syntax of classes of JavaScript and then add its own twists to it. In this article, we’ll look at how to define TypeScript classes and how they inherit from each other. In this article, we’ll look at the access modifiers for class members in TypeScript.
Public, private, and protected modifiers
Public
In TypeScript, class member can have access modifiers added to them. This lets us control the access of the members of class by different parts of the program outside of the class that the members are defined in. The default access modifier for class members in TypeScript is public
. This means that class member that have no access modifiers will be designated as public members. For example, we can use the public
modifier like in the following code:
class Person {
public name: string;
public constructor(name: string) {
this.name = name;
}
public getName(): string{
return this.name;
}
}
const person = new Person('Jane');
console.log(person.getName());
console.log(person.name);
In the example above, we designated all the members in our Person
class as public
so that we can access them outside the Person
class. We can call the getName
method on the Person
instance and also we can get the name
field directly from outside the class. The public
access modifier on the constructor
method is extra because constructor
should always be public so we can instantiate the class with it.
Private and Protected
When a member of a class is marked as private
, then it can’t be accessed outside of its containing class. Protected members are only available from within sub-classes of the class that has the protected member and the class that has the member and is marked with the keyword protected
. For example, if we have a private
member in our class in like the following code:
class Person {
private name: string;
constructor(name: string) {
this.name = name;
}
public getName(): string{
return this.name;
}
}
const person = new Person('Jane');
console.log(person.getName());
console.log(person.name);
Then we get an error when we try to access it like we did with the member name
in the Person
class that we have above. If we try to compile and run the code above, the TypeScript compiler will not compile the code and gives the error message “Property ‘name’ is private and only accessible within class ‘Person’.(2341)“ like we expect for private members.
TypeScript compares type by their structure for public members. If the 2 types have the same public members listed, then they’re marked as being compatible by TypeScript. However, for private and protected members, this isn’t the case. For private and protected members, for 2 classes to be considered equal, then both classes must have the same private and protected members from the same origin for them to be considered to be the same type. For example, if we have the following code:
class Person {
private name: string;
constructor(name: string) {
this.name = name;
}
}
class Human {
private name: string;
constructor(name: string) {
this.name = name;
}
}
const human: Human = new Person('Jane');
Then we would get the error “ Type ‘Person’ is not assignable to type ‘Human’. Types have separate declarations of a private property ‘name’.(2322)“. This means that because both the Person
and Human
have the same private member called name
, that they can’t be considered the same type, so we can’t assign an instance of Person
to a variable that’s of type Human
. This is the same for protected members, so if we have the following code:
class Person {
protected name: string;
constructor(name: string) {
this.name = name;
}
}
class Human {
protected name: string;
constructor(name: string) {
this.name = name;
}
}
const human: Human = new Person('Jane');
We would get the same error. But if we change protected
to public
like we do in the code below, then it would work:
class Person {
public name: string;
constructor(name: string) {
this.name = name;
}
}
class Human {
public name: string;
constructor(name: string) {
this.name = name;
}
}
const human: Human = new Person('Jane');
console.log(human.name);
If we run the code above, we’ll see ‘Jane’ logged from the console.log
statement on the last line.
If we have private members in our classes, then they must be in the super-class for both classes for both classes to be considered equal. For example, we can write the following code to make the Person
and Human
class to be considered the same while having a common private member name
for each class:
class Animal {
private name: string;
constructor(name: string) {
this.name = name;
}
}
class Person extends Animal{
constructor(name: string) {
super(name);
}
}
class Human extends Animal{
constructor(name: string) {
super(name);
}
}
const human: Human = new Person('Jane');
console.log(human);
In the code above, we have the Animal
class that the private name
member, and both the Person
and Human
classes extends the Animal
class, so that the Human
and Person
will be considered equal since they don’t have separate implementations of the private member name
, but rather, a common name
member in the Animal
class instead which they both inherit from. When we run console.log
on human
in the last line, we would see the Person
object being logged.
Likewise, for protected members, we can do the something similar like in the following code:
class Animal {
protected name: string;
constructor(name: string) {
this.name = name;
}
}
class Person extends Animal{
constructor(name: string) {
super(name);
}
getName() {
return this.name;
}
}
class Human extends Animal{
constructor(name: string) {
super(name);
}
getName() {
return this.name;
}
}
const human: Human = new Person('Jane');
console.log(human.getName());
In the code above, we have the protected member name
which can be accessed by its sub-classes Human
and Person
, so we can return the value of the name
member with a getName
method on each class and the value of the name
member in the Animal
class. We need this method because protected members are only available from within sub-classes of the class that has the protected member and the class that has the member. If we run the code above, we would get ‘Jane’ from the console.log
output from the last line of the code above.
In TypeScript, class members can have access modifiers applied to them. Public is the default access modifier for members if nothing is specified. 2 class are considered equal if they both have the same public members, or that they inherit protected and private members from the same source and have the same public members.