Learn TypeScript: Classes, interfaces, and more!
Tags: typescript, programming, productivity
Published: 2022-05-24 21:49:55

Welcome! Today we're going to be brushing up on TypeScript classes, interfaces, inheritance, and other object-oriented programming (OOP) concepts. 

While TypeScript and JavaScript might not be the first languages you think of when it comes to object-oriented programming, you'd be surprised to know just how much support there is for building out complex, robust components in an object-oriented style. The latest versions of TypeScript and JavaScript both introduced changes to their syntax to make it easier for OOP to take place. 

By popular demand, we'll be focusing specifically on OOP concepts in TypeScript today. We'll start with a basic rundown of what classes are, and then move on to concepts like encapsulation, class inheritance, interfaces, and more! 

By the end, you should have a basic understanding of how to implement various OOP concepts in TypeScript. 

**We'll cover**: 
* [What is a class?](#class)
* [Declare a TypeScript class](#declare)
* [Encapsulation with classes](#encapsulation)
    * [Class objects](#objects) 
    * [Class constructors](#constructors)
* [Class inheritance](#inheritance)
* [Access modifiers](#access-modifiers)
    * [Public modifiers](#public)
    * [Private modifiers](#private)
    * [Protected modifiers](#protected)
* [Inheritance with access modifiers](#inheritance-modifiers)
* [Interface and type aliases](#interfaces-type-aliases)
    * [Type aliases](#type-aliases)
    * [Interfaces](#interfaces)
    * [Interfaces and inheritance](#interfaces-inheritance)
* [Wrapping up and next steps](#next)

<a name="class"></a>
## What are classes?

Before the release of ECMAScript 6 (ES6), JavaScript was primarily a functional programming language where inheritance was prototype-based. When the syntax for supporting classes was introduced in 2015, TypeScript quickly adapted to take advantage of object-oriented techniques like encapsulation and abstraction.

TypeScript and JavaScript classes are comprised of the following:
* Constructors
* Properties
* Methods

Classes provide a fundamental structure for creating reusable components in JavaScript. They are an abstraction in object-oriented programming (OOP) languages used to define **objects**, and pass down properties and functions to other classes and objects. 

Objects are data structures made by encapsulating data, and the methods that work on that data. So, you can think of JavaScript classes as **not** being objects in the truest sense, but more similar to a blueprint for objects. 

> **Note**: TypeScript fully supports the class syntax that was introduced in 2015 with the release of ECMAScript 6 (ES6). 

<a name="declare"></a>
## Declare a TypeScript class

**Class declarations** are used to define a class using the `class` keyword along with the class name, and curly braces '{}'.

The `class` keyword:
```
class Fruit {
  // this is an empty class
}
```
**Class expressions** are another way to define a class, but they can be named or unnamed. 

```
let Fruit = class {
  // this class is unnamed
}

let Fruit = class edible_fruits {
  // this class is named 
}
```
You can access named class expressions using the `name` keyword. 

```
let Fruit = class edible_fruits {
  // this class is named 
}

console.log(Fruit.name);
// returns "edible_fruits"
```

<a name="encapsulation"></a>
## Encapsulation with Classes

A key concept of object-oriented programming is **encapsulation**. Encapsulation entails restricting access to an object's state by enclosing data and methods into one unit. Restricting access to certain data or components can be useful for preventing outside code from calling private methods within a specific class. 

TypeScript facilitates encapsulation by enclosing data and its related methods within a class. 

For example, if you have a 'class Student', with two data elements, and a method, you can encapsulate them using the following syntax:

```
class Student {
  name: string=''
  roll: number = 0
  getRoll(): number{
    return this.roll
  }
}
```

<a name="objects"></a>
### Class objects

In JavaScript, objects are variables that can hold a single value.

The variable fruit_one has been assigned a string value of "Apple":
```
let fruit_one = "Apple";
```
Objects are also variables, but instead of a single value, they can be assigned multiple values written as **key : value** pairs. The collection of these key : value pairs make up different properties of the object they belong to. 

A collection of key:value pairs:
```
const fruits = {
  name: "Apple",
  color: "red",
  variety: "Fuji"
};
```
To access class members (data or methods) we have to create an instance of their object. 

In the following example, there are two classes, `Student`, and `School`. Using the object of class `School`, we can try to access a data member of the 'Student' class with `uni.roll`.

Doing so will return a warning that the desired data does not exist in class `School`. Instead, we can fix the code by commenting `uni.roll` and uncommenting `student.roll`.

>Try out a class object for yourself on the [original article on Educative](https://www.educative.io/blog/learn-typescript-classes#objects?eid=5082902844932096)! 

<a name="constructors"></a>
### Class constructors

A class can have a special method or function known as a 'constructor' that gets called automatically when we create an object of that class. A constructor can be used to initialize the class data or perform other actions upon instantiation of its objects. However, it's not necessary for a class to include a constructor. 

Below, the example demonstrates a class 'Car' with a  public constructor that is automatically called upon object instantiation. At the same time, the constructor creates an instance of the class.

```
class Car {
   // define properties
   makeAndModel: string;
   year: number = 0
 
   // constructor of Car
   public constructor() {
       this.makeAndModel = 'Toyota Corolla'
       this.year = 2015
   }
}
 
// create an object of the class
const car = new Car()

 
console.log("\n\n Car make and model : " + car.makeAndModel)
console.log("\n\n Year this " + car.makeAndModel + " was manufactured: " + car.year)

----> Car make and model : Toyota Corolla


 Year this Toyota Corolla was manufactured: 2015
```

<a name="inheritance"></a>
## Class inheritance

Another key concept of object-oriented programming that TypeScript supports is **inheritance**. Inheritance allows us to derive a class from another (parent or super) class, thus extending the parent class's functionality. The newly created classes are referred to as child or sub classes. 

Child classes inherit all properties and methods from their parent class, but **do not** inherit any private data members or constructors. 

You can use the extends keyword to derive child classes from parent classes. 

### Syntax
```
class child_class extends parent_class
```

There are three types of class inheritance:
* **Single**: When one child class inherits class properties and methods from one parent class.
* **Multiple**: When a child class inherits class properties and methods from more than one parent class. TypeScript does not support multiple inheritances.
* **Multi-level**: When a class inherits class properties and methods from another child class (like a grandchild or great-grandchild). 

<a name="access-modifiers"></a>
## Access modifiers

An access modifier restricts the visibility of class data and methods. 

TypeScript provides three key access modifiers: 
* `public`
* `private`
* `protected`

<a name="public"></a>
### Public modifiers

All class members in TypeScript are public by default, but can otherwise be made public using the `public` keyword. These members can be accessed anywhere without restriction when no modifier is specified. 

A public method or data of a class can be accessed by the class itself or by any other (derived or not) class.

Public and default values:

```
class Fruit {
  public fruit_plu: number = 4129;
  fruit_name: string
}

let apple = new Fruit();

apple.fruit_plu = 4129;
apple.fruit_name = "Fuji Apple";

console.log ("\n\n The PLU code for a " + apple.fruit_name + 
            " is " + apple.fruit_plu)

----> The PLU code for a Fuji Apple is 4129
```
In the example above, 'fruit_plu' and 'fruit_name' keywords are both considered to be public class members even though 'fruit_plu' is the only one with an access modifier in front of it. Remember, when you don't specify the **scope** of a class member, it can be accessed from outside of the class. 

<a name="private"></a>
### Private modifiers

A `private` method or data member of a class cannot be accessed from outside of its class. You can use the `private` keyword to set its scope. When the scope of a private member is limited to its class, only other methods in that same class can have access to it. 

```
class Fruit {
  private fruit_plu: number = 4129;
  fruit_name: string
}

let apple = new Fruit();

// running this console.log should return an error because it is inaccessible
console.log("\n\n The PLU code for this fruit is " + apple.fruit_plu)

----> index.ts(9,60): error TS2341: Property 'fruit_plu' is private and only accessible within class 'Fruit'.
```
In the example above, attempting to access the `fruit_plu` member will return a compiler error because its scope has been set to `private`. You will still be able to access the `fruit_name` member because its scope is by default, `public`.

<a name="protected"></a>
### Protected modifiers

A protected access modifier works similarly to the private access modifier, with one major exception. The protected methods or data members of a class can be accessed by the class itself and also by any child class derived from it. With TypeScript, you can use the constructor keyword to declare a public property and a protected property in the same class. These are parameter properties and can allow you to declare a constructor parameter and a class member at the same time. 

> **Note**: One caveat of TypeScript’s type system is that the private and protected scopes are only enforced during runtime type checking.

<a name="inheritance-modifiers"></a>
## Inheritance with access modifiers

Now that you have a basic understanding of inheritance and access modifiers, we can demonstrate how these two concepts can be used to modify access to some members of a class but not others.  

In the following example, you'll see that the scope of all class members is readily accessible. You should be able to execute this program without returning any errors. 

However, we have also included code that alters the scope of different class members as comments. Visit the [original article on Educative](https://www.educative.io/blog/learn-typescript-classes#inheritance-modifiers?eid=5082902844932096) to practice uncommenting different sections of the code to see how access is enforced by different access modifiers! You'll also get to see what kind of errors are returned by TypeScript. 

```
// 
// Base class
class Car {
   protected makeAndModel: string;
   private year: number = 0
 
   // constructor of Car
   public constructor() {
   }
 
   //getter of make and model

   public getMakeAndModel (): string {
       return this.makeAndModel;
   }
 
   //setter of make and model
   public setMakeAndModel(make: string) {
       this.makeAndModel = make;
   }
 
   //getter of manufacture year
   public getYear (): number {
       return this.year;
   }
  
   //setter of manufacture year
   public setYear(year: number) {
       this.year = year;
   }
}
 
// Derived class
class Tesla extends Car {
   //public data member, for the car location.
   public location: string
 
   //constructor of Tesla class
   constructor() {
       super();
       super.makeAndModel = 'Tesla X'
   }
 
   /** Uncommenting the following will give error, because */
   /** year is defined private (instead of public or protected) */
   /*
   getYear (): number {
       return this.year;
   }
   */
}
 
// create objects of each class.
const tesla = new Tesla()
const car = new Car()

//setYear is public hence can be accessed from derived class
tesla.setYear(2022)
tesla.location = 'New York'
 
console.log("\n\nMake and model of car: " + tesla.getMakeAndModel())
console.log("\n\nLocation: "+ tesla.location);
 
// Uncommenting the following code should give error because
//location is first defined in the derived class
//car.location = "San Francisco"
 
 
 // Uncommenting the following code should give error because
//year is a private variable.
//tesla.year = 2001;

----> Make and model of car: Tesla X


Location: New York
```
<a name="interfaces-type-aliases"></a>
## Interfaces and type aliases

<a name="type-aliases"></a>
### Type Aliases

There are two ways to define types for your data in TypeScript: **type aliases** and **interfaces**. 

Type aliases are declared using the `type` keyword, and are used to explicitly annotate a type with a name (alias). Type aliases can be used to represent primitive data types like string or boolean, but they can also be used to represent `object` types, `tuples`, and more! 

Unlike interfaces, type aliases cannot be declared more than once, and cannot be changed after being created.

> **Note**: Fun fact! The TypeScript compiler converts TypeScript **entirely** into JavaScript code during a process called **transpilation**. This is to ensure that any and all JavaScript programs are fully compatible with the TypeScript programming language. 

<a name="interfaces"></a>
### Interfaces

Interfaces are another way to define the data structure of your objects. It is an abstract type that tells the TypeScript compiler which properties a given object can have. In TypeScript, interfaces provide the syntax for an object to declare properties, methods, and events, but it's up to the deriving class to define those members. Unlike type aliases, you can freely add new fields to an existing interface.

You can declare an interface using the `interface` keyword. In this example, we can define an interface for an apple with the properties 'variety' and 'color' as strings. 

```
interface Apple {
  variety: string;
  color: string
}
```

To implement the `Apple` interface, we simply assign values to the properties. 

```
interface Apple {
  variety: string;
  color: string
}

let newFruit: Apple = {
    variety: "Opal",
    color: "yellow",
};

console.log("\n\n We have a new type of apple in stock! It's " + newFruit.color + " and of the "
             + newFruit.variety + " variety.")

----> We have a new type of apple in stock! It's yellow and of the Opal variety.
```

Interfaces can be thought of as a blueprint for the data structure that deriving classes must follow. In the example below, we'll look at how to implement an interface with a class using the `implements` keyword.

```
// Interface as a blueprint
interface IStudent {
   roll: number
   stdName: string
   // ? implies the data item is a derived class
   // may or may contain
   stdDOB?: string
   // Method declaration (no body) in the interface
   getFathersName():string;
}
 
// A class implementing interface
class BSStudent implements IStudent {
   roll: number
   stdName: string
   fathersName: string
 
   getFathersName():string {
       return this.fathersName
   }
}
 
// Create an object of the class.
const std  = new BSStudent ()
std.fathersName = 'Bob'
std.roll = 33

std.stdName = "John"
 
console.log("\n\n Student's name = "+std.stdName)
console.log("\n\n Father's name = "+std.fathersName)
console.log("\n\n Roll number = "+std.roll)

----> Student's name = John


 Father's name = Bob


 Roll number = 33
```
### Interfaces and inheritance

Just like with classes, we can derive an interface from other interfaces through inheritance. Unlike classes, a single interface may be extended from **multiple** interfaces. 

In the example below, we demonstrate how the interface `ITeacherAndStudent` can be derived from the `IStudent` and `ITeacher` interfaces. 

```
// Interface for students
interface IStudent {
   roll: number
   stdName: string
   getFathersName():string;
}
 
// Interface for teachers
interface ITeacher {
   id: number
   name: string
}
 
// An interface derived from both students and teachers.
interface ITeacherAndStudent extends IStudent, ITeacher {
   age: number
}
```
<a name="next"></a>
## Wrapping up and next steps

Great job! By now, you should have a basic understanding of TypeScript classes, interfaces, and how to use them alongside other object-oriented programming concepts. That said, you don't have to stop learning quite yet. TypeScript is a powerful tool for making JavaScript easier for collaborative efforts. There is a huge library of fantastic resources that are available for developers of all levels. 

To help you master TypeScript, we've created the [**TypeScript for Programmers**](https://www.educative.io/path/typescript-for-programmers?eid=5082902844932096) learning path to help you build advanced TypeScript programming skills. 

*Happy learning!*

### Continue reading about TypeScript on Educative
* [TypeScript enum guide: Get started in 5 minutes](https://www.educative.io/blog/typescript-enum-guide?eid=5082902844932096)
* [TypeScript Tutorial: A step-by-step guide to learn TypeScript](https://www.educative.io/blog/typescript-tutorial?eid=5082902844932096)
* [Understanding Advanced Concepts in TypeScript](https://www.educative.io/blog/advanced-typescript-concepts?eid=5082902844932096)
* [A Simple Guide to TypeScript Interfaces: declaration & use cases](https://www.educative.io/blog/typescript-interfaces?eid=5082902844932096)

### Start a discussion
What do you hope to do with TypeScript? Was this article helpful? Let us know in the comments below!