# Classes in TypeScript

Looking back to our last article, we covered **Functions in TypeScript** which gave us an idea about how Functions work, are structured and are implemented in TypeScript.

In this article, we will discuss the syntax of creating classes, the different features available, how classes are treated during compile-time type-check, access modifiers, shorthand initialization, how Getters and Setters work, static properties/methods, abstract class, and Singleton pattern.

![class.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1644259092142/fq7h-XChg.png align="left")

## WHAT IS A CLASS?

> **Classes are a common abstraction used in object-oriented programming (OOP) languages to describe data structures known as objects. These objects may contain an initial state and implement behaviors bound to that particular object instance.**

## WHAT TYPESCRIPT ADDS?

> **In 2015, ECMAScript 6 introduced a new syntax to JavaScript to create classes that internally use the prototype features of the language.**

> **TypeScript has full support for that syntax and also adds features on top of it, like member visibility, abstract classes, generic classes, arrow function methods, access modifiers, and a few others.**

## SYNTAX, DECLARATION & INSTANCE CREATION:

The syntax is mostly the same used to create classes with JavaScript. But there are some distinguishing features available in TypeScript.

### CREATE A CLASS:

You can create a class declaration by using the `class` keyword, followed by the class name and then a `{}` pair block, as shown in the following code:

```typescript
class Course {

}
```

### CREATE AN INSTANCE USING `new` KEYWORD:

The following snippet creates a new class named `Course`. You can then create a new instance of the `Course` class using the `new` keyword followed by the name of your class and an empty parameter list, as shown below:

```typescript
class Course {
 
}

const courseInstance = new Course();
```

You can think of the `Course` class as a blueprint for creating objects and the `courseInstance` as one of those objects.

### TYPE OF THE CREATED INSTANCE:

As you hover over the `courseInstance` variable, which is an instance of the class `Course`, you will see that TypeScript has inferred the type of the variable as `Course`.:

![image.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1644779688881/o1A4c61Np.png align="left")

So, the class can also be considered as a `type` in TypeScript.

## `constructor` FUNCTION & CLASS PROPERTIES:

Most of the time, when working with classes, you will need to create a `constructor` function. A `constructor` is a method that runs every time a new instance of a class is created (**Not when you declare a class**). It can be used to initialize values/fields in the class.

A class can hold variables called properties which can be initialized inside a `constructor` function as follows:

```typescript
class Course {
  title: string;

  constructor(n: string) {
    this.title = n;
  }
}
```

Here, we are declaring a `title` variable/property of type `string` and initialize it within the constructor function.

When the constructor accepts a parameter, we need to pass it when creating an instance. The compilation error we will receive if we don't pass is as follows:

![image.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1644300772888/gWA1mkTLv.png align="left")

The `Course` class is accepting a variable of type `string` which is mandatory. Hence, it must be passed when creating an instance as follows:

```typescript
class Course {
  title: string;

  constructor(n: string) {
    this.title = n;
  }
}

const courseInstance = new Course("Typescript");
```

Now that `courseInstance` represents an instance of the class `Course`, you can access fields, methods, and properties of the class `Course` via this variable as follows:

```typescript
class Course {
  title: string;

  constructor(n: string) {
    this.title = n;
  }
}

const courseInstance = new Course("Typescript");

console.log(courseInstance.title); // This will print Typescript
```

## CLASS METHODS & `this` KEYWORD:

Similar to properties, the class also can hold methods that are nothing but functions declared inside a class.

Let's take an example to understand the method:

```typescript
class Course {
  title: string;

  constructor(n: string) {
    this.title = n;
  }

  describe() {
    console.log(`This is a ${this.title} course`);
  }
}
```

In the above code snippet, we are declaring a `describe` method inside our `Course` class which is not accepting any parameters.

Inside it, we are printing a string `This is a ${this.title} course`. Here, the `this` keyword refers to the concrete instance of `Course` *(*`courseInstance`). If you only specified `title` then it will throw a compilation error as follows:

![image.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1644300811764/poQz8sB40.png align="left")

Thus, if you want to access any property or method of the `Course` class, you have to use the `this` keyword.

Now, let's call `describe` and see what we get in the console.

```typescript
class Course {
  title: string;

  constructor(n: string) {
    this.title = n;
  }

  describe() {
    console.log(`This is a ${this.title} course`);
  }
}

const courseInstance = new Course("TypeScript");

courseInstance.describe();
```

![image.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1644302544337/-O4Um-ZbC.png align="left")

## INHERITANCE:

### WHAT IS INHERITANCE?

* **Inheritance is an aspect of OOPs languages, which provides the ability of a program to create a new class from an existing class. It is a mechanism that acquires the properties and behaviors of a class from another class.**
    
* **The class whose members are inherited is called the base class, and the class that inherits those members is called the child class.**
    

### `extends` KEYWORD:

To implement inheritance we have to use the `extends` keyword as follows:

```typescript
class Animal {

}

class Dog extends Animal {
  
}
```

In the above code snippet, we are declaring a **parent class** `Animal` and a **child class** `Dog`. Using the `extends` keyword we are inheriting properties and methods of an `Animal` class inside a `Dog` class.

### `super` KEYWORD:

When you inherit any class, you must call the `super` method inside the constructor of the child class. `super` invokes the parent constructor and its values/parameters.

Let's take an example to understand this:

```typescript
class Animal {
  name: string;

  constructor(n: string) {
    this.name = n;
  }
}

class Dog extends Animal {
  constructor(dogName: string) {
    super(dogName);
  }
}

const dog = new Dog("Tuffy");
```

A **parent class** `Animal` has a `name` property and its constructor also expects a parameter named `n` of type `string`.

Since the `Dog` class inherits the parent class `Animal`, whenever we create an instance of this class we must call the parent class `constructor` function. This is done by calling `super(dogName)`, which invokes the `constructor` of the `Animal` class with `dogName` as a parameter that the constructor of the `Animal` class expects.

A compilation error will occur if we do not pass any value inside `super()` because the `Animal` class expects a mandatory parameter `n` of type `string`.

![image.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1644525538806/HN8Af21TU.png align="left")

In addition, since the `Animal` class expects a `string` type, we must pass `string` only if we pass a `number` we will get the following compilation error.

![image.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1644525672284/4FTP0Uq8h.png align="left")

In short, whenever you inherit any class you have to call `super` to invoke its constructor.

## ENCAPSULATION & ACCESS MODIFIERS:

### WHAT IS ENCAPSULATION?

* **Encapsulation enables you to perform what’s called “data hiding”. It’s necessary to hide certain data so that it’s not changed accidentally or purposefully by other components or code in the program.**
    
* **To achieve encapsulation, use the proper access modifiers combined with the proper member types to limit or expose the scope of data.**
    
* **Access modifiers are markers on code that designate where that code can be used, and are as follows:**
    

### `public` MODIFIER:

`public` fields do not encapsulate since any calling code can modify the data at any time. If you declare a property/method without an access modifier, it is a `public` property/method.

Basically, `public` members are accessible everywhere without restrictions.

Let's take an example to understand more:

```typescript
class Course {
  public title: string; 

  constructor(n: string) {
    this.title = n;
  }
}

const course = new Course('Angular');

console.log(course.title); // Prints: Angular
```

All TypeScript members (properties and methods) are `public` by default, so you don't need to prefix them with the `public` keyword.

You can also modify the values of the `public` properties. Look at the following code:

```typescript
class Course {
  public title: string; 

  constructor(n: string) {
    this.title = n;
  }
}

const Course1 = new Course("Angular");

Course1.title = "TypeScript";

console.log(Course1.title); // Prints: TypeScript
```

### `private` MODIFIER:

A `private` property/method cannot be accessed outside of its containing class. `private` properties and methods can only be accessed within the class.

Let's take an example to understand more:

```typescript
class Course {
  private title: string; 

  constructor(n: string) {
    this.title = n;
  }
}

const course = new Course('Angular');

console.log(course.title); // Throws compilation error
```

The above code has a `title` property which is a `private` property of the class `Course`.

Accessing it outside of the `Course` class will result in the following compilation error:

![image.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1644607584436/6CcbSYiyL8.png align="left")

The child class does not even have access to `private` properties. Consider the following code:

```typescript
class Course {
  private title: string;

  constructor(n: string) {
    this.title = n;
  }
}

class PaidCourse extends Course {
  constructor() {
    super("Paid Course");
    console.log(this.title);
  }
}
```

In the above code, we have a `Course` class as a parent and a `PaidCourse` class as a child. We learned earlier that properties can be inherited from the parent class to the child class except for `private` properties. So the above code will result in the following compilation error:

![image.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1644607803970/VRLMUT2pd.png align="left")

### `protected` MODIFIER:

A `protected` member cannot be accessed outside of its containing class. Members that are `protected` can only be accessed within the class and its child classes.

In the above code, if we change the `title` property of the `Course` class from `private` to `protected`, the compilation error will be resolved. Check out the following image:

![image.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1644608209885/uYM2ZFGGp.png align="left")

You will, however, receive a compilation error if you try to access the `protected` `title` property through an instance of the `Course` class. See the following code:

```typescript
class Course {
  protected title: string; // only accessible within the class and child classes

  constructor(n: string) {
    this.title = n;
  }
}

class PaidCourse extends Course {
  constructor() {
    super("Paid Course");
    console.log(this.title); // prints "Paid Course" (valid line)
  }
}

const course = new Course("Another course"); 

console.log(course.title); // throws error
```

Because the `title` is a `protected` property, we cannot access it through the instance. We will receive the following compilation error:

![image.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1644608408719/MqBUlFGCu.png align="left")

### `readonly` MODIFIER:

TypeScript supports `readonly` modifiers **on the property level** by using the `readonly` keyword. The `readonly` properties must be initialized at their declaration or in the constructor.

Take a look at the following code to understand this:

```ts
class Course {
  readonly price: number;
  constructor(p: number) {
    this.price = p;
  }

  changePrice(p: number) {
    this.price = p; // throws error because price is readonly
  }
}

const Course1 = new Course(10);

Course1.changePrice(20);
```

We have a `readonly` `price` property in the above code, which can only be assigned once, inside the constructor function.

You will get a compilation error as follows if you try to change it anywhere else:

![image.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1644608870265/r8yQXozRf.png align="left")

## SHORTHAND INITIALIZATION:

In previous examples, we declared properties first and then initialized them inside the `constructor` as follows:

```typescript
class Course {
  title: string;
  price: number;
  ratings: number;

  constructor(title: string, price: number, ratings: number) {
    this.title = title;
    this.price = price;
    this.ratings = ratings;
  }
}
```

The above code first declares three properties and sets/assigns them inside a `constructor` function.

It is possible to write less and more readable code by simplifying and using a shortcut, as follows:

```typescript
class Course {
  constructor(
    public title: string,
    public price: number,
    public ratings: number
  ) {}
}
```

In this case, Typescript will automatically generate those properties. Based on your requirements, you can use any other access modifier instead of `public`.

Unless you use access modifiers, TypeScript will just consider it as a parameter and not generate a property.

For example, in the above code, if we use `public` only for `title` and `amount`, and pass `ratings` without any access modifier, it will throw an error if you attempt to access `ratings` the following way:

![image.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1644610017195/r-KsGPjRb.png align="left")

So you have to use an access modifier to make `shorthand initialization` valid.

## GETTERS & SETTERS:

**Getters** and **Setters** are nothing more than methods that provide access to an object's properties.

They allow us to hide implementation details from instance objects. Thus, we can do some operations inside getters and setters that are completely encapsulated.

* `getter`: Use this method when you want to access any property of an object. A getter is also called an accessor.
    
* `setter`: Use this method when you want to change any property of an object. A setter is also known as a mutator.
    

A getter method starts with the keyword `get` and a setter method starts with the keyword `set`.

Let's look at Getters and Setters one by one with examples:

### GETTER METHODS:

To understand the syntax and basic concept, let's take an example:

```typescript
class Person {
  firstName: string;
  lastName: string;
  constructor(f_name: string, l_name: string) {
    this.firstName = f_name;
    this.lastName = l_name;
  }

  getFullName() {
    return this.firstName + " " + this.lastName;
  }
}

const person = new Person("John", "Doe");
console.log(person.getFullName()); // prints "John Doe"
```

In the above example, we have a class called `Person` that accepts `f_name` and `l_name` parameters and has `firstName` and `lastName` properties.

It also has a `method` called `getFullName()` that combines `firstName` and `lastName` to return a full name.

Instead of using the `getFullName` method, we can use getter as follows:

```typescript
class Person {
  firstName: string;
  lastName: string;
  constructor(f_name: string, l_name: string) {
    this.firstName = f_name;
    this.lastName = l_name;
  }

  get fullName() {
    return this.firstName + " " + this.lastName;
  }
}

const person = new Person("John", "Doe");

// we dont execute getters
console.log(person.fullName); // prints "John Doe"
```

Using the `get` keyword, we declare a getter method. Notice that we don't execute or use `()` after `person.fullName` as TypeScript infers it as a `readonly` property. Hence, you cannot modify it. In doing so, you will receive the following error:

![image.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1644692283524/h5x6zuS-_.png align="left")

In the first example, the method `getFullName` does the same thing as the getter method. However, the getter method is slightly simpler and it is easier to identify its purpose at a glance with its syntax.

### SETTER METHODS:

A setter method is used to mutate the value of a class member.

In our previous example, there was no way to replace a person's name other than to create a new object.

This provides us with class safety, but what if we need to change the `firstName`.

The setter method in TypeScript allows us to change the value of a property. ***(even if the value is*** `private` or `protected`)

To write a setter method, we use the keyword `set` before the method name.

```typescript
class Person {
  private firstName: string;
  private lastName: string;
  constructor(f_name: string, l_name: string) {
    this.firstName = f_name;
    this.lastName = l_name;
  }

  get fullName(): string {
    return `${this.firstName} ${this.lastName}`;
  }

  set setFirstName(f_name: string) {
    this.firstName = f_name;
  }
}

const person = new Person("John", "Doe");

// Whatever value we assign to the setter, it will be passed to the setter as a parameter.
person.setFirstName = "Rohan"; // Setting the firstName property

console.log(person.fullName); // prints "Rohan Doe"
```

We have a class called `Person` with two `private` properties called `firstName` and `lastName`. Additionally, there is a getter method to retrieve a person's full name and a setter method to change the `firstName`.

**IMPORTANT: Setter functions can take only one parameter. You will receive a compilation error if you try to pass more than one.**

![image.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1644693283920/LMGELfaFo.png align="left")

## STATIC PROPERTIES & METHODS:

* Static members can be accessed without having the class instantiated. Of course, it depends on which access modifier you are using.
    
* So `public` static members can be accessed directly from outside the class.
    
* `private` static members can only be used within the class, and
    
* `protected` members can be accessed by the class in which the member is defined as well as by its child classes.
    

### HOW TO ACCESS INSIDE A CLASS:

When working with the `static` properties/methods you have to use the class itself and not the instance of the class.

Check out the following code to see how to access them inside a class:

```typescript
class Mathematics {
  static PI = 3.14159; // static property

  calculateCircumference(diameter: number): number {
    return this.PI * diameter; // this.PI is not accessible here because it is a static property
  }
}
```

Here, `PI` is a `static` property and `calculateCircumference` is a normal method in the `Mathematics` class.

Therefore, if you try to access it inside of a class method by using the `this` keyword, you will receive an error because `PI` is **NOT** a class property. Rather, it is a `static` property.

![image.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1644746104710/Ck88BXYv8.png align="left")

To remove the error, we need to access the `static` property using the class name:

```typescript
class Mathematics {
  static PI = 3.14159; // static property

  calculateCircumference(diameter: number): number {
    return Mathematics.PI * diameter;
  }
}
```

**NOTE: You can only use the** `this` keyword inside a `static` method if you want to access the `static` property. Therefore, if you make `calculateCircumference` a `static` method then `this.PI` would be valid:

![image.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1644746278545/mMG--PMMy.png align="left")

### HOW TO ACCESS OUTSIDE A CLASS:

Check out the following code to see how to access them outside a class:

```typescript
class Mathematics {
  static PI = 3.14159; // static property

  // static method
  static calculateCircumference(diameter: number): number {
    return Mathematics.PI * diameter;
  }
}

let newMath = new Mathematics();

console.log(newMath.PI); // This will throw an error
console.log(newMath.calculateCircumference(10)); // This will throw an error

console.log(Mathematics.PI); // returns 3.14159
console.log(Mathematics.calculateCircumference(10)); // returns 31.4159
```

If you try to access `static` property `PI` or `static` method `calculateCircumference` using `newMath` in the above code, you will get the following compilation error:

![image.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1644745530942/rjUUiAQyS.png align="left")

In addition, TypeScript also detects that the `PI` and `calculateCircumference` are static properties and methods respectively and asks `"Did you mean to access the static member 'Mathematics.calculateCircumference' instead?"`.

### INBUILT `Math` CLASS:

* TypeScript's `Math` class provides no. of properties and methods for performing mathematical operations.
    
* `Math` is not a constructor and all the properties and methods of `Math` are `static`.
    

```typescript
function mathTest(num: number): void {
  var squareRoot = Math.sqrt(num); // sqrt is a static method of Math
  console.log("Random Number:  " + num); // logs random number
  console.log("Square root:  " + squareRoot); // logs Square root of random number
  console.log("PI value: ", Math.PI); // PI is a static property of Math
}
var randomNum: number = Math.random(); // random is a static method of Math
mathTest(randomNum);
```

## ABSTRACTION & ABSTRACT CLASS:

* **Abstract classes can have implementation details for their members. To declare an abstract class, we can use the** `abstract` keyword. We can also use the `abstract` keyword for methods to declare `abstract methods`, which are implemented by classes that derive from an abstract class.
    
* **In short abstract classes are classes that have a partial implementation of a class from which other classes can be derived. It's a blueprint for classes.**
    
* **They can’t be instantiated directly.**
    

### SYNTAX AND ABSTRACT METHODS:

Let's take an example to understand the syntax and implementation:

```typescript
abstract class Person {
  name: string;
  age: number;
  constructor(name: string, age: number) {
    this.name = name;
    this.age = age;
  }
  abstract getName(): string;
  abstract getAge(): number;
}
```

In the above code, we have declared a `Person` class prefixed with the `abstract` keyword which means it is an abstract class.

We have `name` and `age` as properties for the `Person` class. It also has the `abstract` methods `getName` and `getAge`. But if you see these methods don't have implementations in them we have just declared them with their return type.

Because abstract methods don’t contain implementations of the method. It’s up to the child classes that inherit the abstract class to implement the method listed. They may also, optionally, include access modifiers.

### INHERIT ABSTRACT CLASS:

Let's inherit the above class:

```typescript
abstract class Person {
  name: string;
  age: number;
  constructor(name: string, age: number) {
    this.name = name;
    this.age = age;
  }
  abstract getName(): string;
  abstract getAge(): number;
}


class Employee extends Person{
  constructor(name: string, age: number) {
    super(name, age);
  }
  getName() {
    return this.name;
  }
  getAge() {
    return this.age;
  }
}
```

As we can see, in the `Person` class the abstract methods only have signatures in them. The actual implementation of the methods is in the `Employee` class, which extends the `Person` class.

If you miss any one of the abstract methods inside an `Employee` class you will get a compilation error as follows:

![image.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1644752217784/OSrVjsgLF.png align="left")

TypeScript checks the method declaration and the return type of the method in the abstract class, so we must be implementing the abstract methods as it’s declared in the abstract class.

This means that in the example above, the `getName` method must take no parameters and must return a `string`. If you try to break those type checks you will get an error:

![image.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1644752459462/Pco7KAkX0.png align="left")

Likewise, the `getAge` method must take no parameters and must return a `number`.

After the abstract methods have been implemented, we can call them normally like any other method as follows:

```typescript
let employee = new Employee("Jane", 20);
console.log(employee.getName()); // returns "Jane"
console.log(employee.getAge()); // returns 20
```

## SINGLETON PATTERN:

* **Singleton is a creational design pattern, which ensures that only one object of its kind exists and provides a single point of access to it for any other code.**
    
* **It is a way to structure your code so that you can’t have more than one instance of your logic, ever.**
    

### SINGLETON CLASS IN TYPESCRIPT:

We can use access modifiers on TypeScript constructors, so we can now create singletons as we do in other languages.

A singleton class's constructor is `private`, which means it cannot be used outside of the class. Therefore, we can't create an instance of that class with the `new` keyword.

To understand this let's create a singleton class:

```ts
class SingletonClass {
  private constructor() {
    console.log("SingletonClass created");
  }
}

const singletonClass = new SingletonClass(); // throws error
```

In the above code, we have a class called `SingletonClass`, which has a `private` `constructor`. So, when we try creating an instance from that class, we receive the following error:

![image.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1644778298243/7Pvva43EE.png align="left")

The above error indicates that we can only access the constructor within the class itself.

To make it a singleton class, we need to create an instance within the class as follows:

```typescript
class SingletonClass {
  private static instance: SingletonClass;

  // only accessible within the class
  private constructor() {
    console.log("SingletonClass created");
  }


  static getInstance() {
    if (SingletonClass.instance) {
      return SingletonClass.instance;
    }
    SingletonClass.instance = new SingletonClass();
    return SingletonClass.instance;
  }
}

const singletonClass = SingletonClass.getInstance(); // creates a new instance
```

The above code declares a `private` and `static` property named `instance` that is of type `SingletonClass`.

We have declared a `static` method `getInstance` that first checks if an instance of the class `SingletonClass` already exists or not. Upon success, it will return the same instance otherwise a new instance will be created.

Therefore, if you call `getInstance` multiple times, the constructor function will only be executed once, as shown in the following image:

![image.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1644779278499/LljmhRrA_.png align="left")

### USE CASES:

* The purpose of the singleton class is to control object creation, limiting the number of objects to only one.
    
* The singleton allows only one entry point to create a new instance of the class.
    
* The use of singletons is often useful when we need to control resources, such as database connections or sockets.
    

## CONCLUSION:

* TypeScript classes are even more powerful than JavaScript classes because they have access to the type system and new features such as member visibility, access modifiers, abstract classes, and much more.
    
* In this way, you can deliver code that is type-safe, more reliable, and more representative of your business model.
    

Make sure to subscribe to our newsletter on [https://blog.wajeshubham.in/](https://blog.wajeshubham.in/) and never miss any upcoming articles related to TypeScript and programming just like this one.

I hope this post will help you in your journey. Keep learning!

My [***Website***](https://wajeshubham.in), connect with me on [LinkedIn](https://www.linkedin.com/in/shubham-waje/) and [GitHub](https://github.com/wajeshubham).
