# Interfaces in TypeScript

Last [article](https://blog.wajeshubham.in/classes-in-typescript), we talked about TypeScript classes, how to use them effectively, and all of the fundamental concepts associated with them.

In this article, we’ll create interfaces, learn how to use them, explore the differences between normal types and interfaces, and learn about declaration and merging.

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

## WHAT IS AN INTERFACE?

> **An Interface is a structure that acts as a contract/doc in our application. It defines the syntax for classes/objects to follow, which means a class/object which implements an interface is bound to implement all its members (except for optional properties/methods)**.

> **The interface contains only the declaration of the methods and fields, but not the implementation. It is a shape of an object.**

## SYNTAX & DECLARATION:

### CREATE AN INTERFACE:

Interfaces in TypeScript are created by using the `interface` keyword followed by the name of the interface, and then a `{}` block with the body of the interface. For example, here is a `Course` interface:

```typescript
interface Course {
  name: string;
  price: number;
  isFree: boolean;
  onPurchase: (userId: string) => void;
}
```

Similar to creating a normal `type` using the type declaration, you specify the fields/properties of the `interface`, and their types, in the `{}`:

The above `Course` interface represents an object that has four properties which are `name` of type `string`, `price` of type `number`, `isFree` of type `boolean`, and `onPurchase` which is a function that accepts a single parameter `userId` of type `string` and returns `void`.

### USE THE INTERFACE AS A VALID TYPE:

Now the `Course` interface is a valid TypeScript type. It can be used like any other type. An example of creating an object literal that matches the `Course` interface is as follows:

```typescript
let course: Course = {
  name: "Angular",
  price: 100,
  isFree: true,
  onPurchase: (userId: string) => {
    console.log(`User with id ${userId} has purchased ${course.name}`);
  },
};
```

Variables using the `Course` interface as their type must have the same members as those specified in the `Course` interface declaration.

If you miss any one of the members you will get a compilation error as follows:

![image.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1645212450441/X4Y-HQgpa.png align="left")

### OPTIONAL PROPERTIES:

If you want to resolve the above error, you must include the property or make the particular property `optional` by adding `?` ahead of `:` as follows:

```typescript
interface Course {
  name: string;
  price: number;
  isFree: boolean;
  onPurchase?: (userId: string) => void; // this is an optional property
}

// No compilation error
let course: Course = {
  name: "Angular",
  price: 100,
  isFree: true,
};
```

However, when you make any property optional, TypeScript automatically infers that property to be of type `<your given type> | undefined`.

If you hover over the `onPurchase` property, you'll see that TypeScript has inferred the type as `((userId: string) => void) | undefined`.

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

To avoid compile-time errors when working with such properties, type guards must be used. Type guards are nothing more than extra type-checking.

> We will discuss Type guards in depth in future blogs.

### READONLY PROPERTIES:

You can add read-only properties to an interface using the `readonly` keyword. This is similar to the last time we discussed how to create read-only properties inside a class with the `readonly` modifier.

Whenever we want to declare a property as read-only, we put the `readonly` keyword before the name of the property inside an `interface`, as shown below:

```typescript
interface Course {
  name: string;
  readonly price: number; // readonly property
  isFree: boolean;
  onPurchase?: (userId: string) => void; // this is an optional property
}

// No compilation error
let course: Course = {
  name: "Angular",
  price: 100,
  isFree: true,
};

course.name = "Angular updated name"
course.price = 150; // throws compilation error
```

`price` is a `readonly` property in the above code, so you can only provide a value while declaring a variable, you can't modify it afterward. Doing so will result in the following compilation error:

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

**NOTE: In an interface, you can only use the** `readonly` modifier. You CAN NOT use other modifiers like `private`, `public`, or `protected` inside an `interface`.

## IS INTERFACE A THING IN JAVASCRIPT?

* **In JavaScript, the interface is not a thing**. They are used to define a type of object or implement it in a class, **but only in TypeScript.**
    
* TypeScript Interface has zero JavaScript code which means it is only available in TypeScript and does not produce any code in compiled JavaScript files. This is also known as "duck typing" or "structural subtyping".
    

To understand the second point let's take an example:

In our first blog, we learned that whenever our typescript is compiled, it's converted into JavaScript. Therefore, let us create an interface and write some logic to see what the equivalent code looks like in JavaScript.

Take a look at the following code:

> For this, you need a basic project setup. Check out my blog on [Introduction to Typescript](https://blog.wajeshubham.in/introduction-to-typescript#heading-installation) which includes detailed instructions on how to compile TS files into JS files including the tools and libraries necessary.

Create an `index.ts` file and add the following code to it:

```typescript
interface Course {
  name: string;
  price: number;
  isFree: boolean;
}

let course: Course = {
  name: "Angular",
  price: 100,
  isFree: true,
};

console.log("Created course: ", course);
```

Then, run the following command to compile the `index.ts` into JavaScript:

```bash
tsc index.ts
```

This will generate an `index.js` file that contains equivalent JavaScript code. If you open that file, you will see that there is no trace of an `interface`. Instead, you will see only a variable declaration and a console log statement. Here is an example of what that would look like:

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

The reason is that JavaScript cannot understand interfaces. An `interface` is a development feature only found in TypeScript. It aims to improve code quality by adding type checks, preventing compilation errors, and ensuring bug-free code.

> Some libraries help create interfaces in JavaScript, such as [`implement.js`](https://www.npmjs.com/package/implement-js), but if you want them, why not use TypeScript instead?

## USING INTERFACES WITH FUNCTIONS:

The use of interfaces in functions generally refers to assigning types to the parameters and explicitly indicating the type of the return value.

### INTERFACE FOR FUNCTION PARAMETERS:

To avoid repeating complex types for multiple variables/parameters, we can create an interface for the parameter of the function and use the interface as a type.

Take a look at the following code:

```typescript
interface Course {
  name: string;
  price: number;
  isFree: boolean;
}

let courses: Course[] = [];

const addCourse = (course: Course): void => {
  courses.push(course);
};

addCourse({
  name: "Angular",
  price: 100,
  isFree: true,
});

console.log(courses); // prints [{name: "Angular", price: 100, isFree: true}]
```

Like our previous examples, we have a `Course` interface with some properties. Additionally, we have an `addCourse` function that expects a parameter of type `Course`. Using `addCourse` we append the `course` parameter to `courses`, a variable that has a type of `Course[]` *(An array of elements with the type* `Course`*)*.

When we use interface as a type for the parameter, we avoid writing the same and lengthy type structure over and over again as well as getting autocompletion when we make any operation on the parameter, as shown below:

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

### INTERFACE FOR FUNCTION RETURN TYPE:

The interface can also be used as a function return type, as follows:

```typescript
interface Course {
  name: string;
  price: number;
  isFree: boolean;
}

let courses: Course[] = [];

const addCourse = (course: Course): void => {
  course.name = course.name.toUpperCase();
  courses.push(course);
};

// function with return type Course[] (Array of Course)
const getCourses = (): Course[] => {
  return courses;
};

addCourse({
  name: "Angular",
  price: 100,
  isFree: true,
});

let coursesArray = getCourses();
```

A new function called `getCourses` is added here to return the `courses` array, which is of type `Course[]`. This can be specified as a return type.

If you don't explicitly specify the return type of `getCourses`, TypeScript will infer it as a `Course[]` as follows:

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

## USING INTERFACES WITH CLASSES:

In our last blog, [Classes in typescript,](https://blog.wajeshubham.in/classes-in-typescript) we took a deep dive into class concepts.

We will now look at how we can make classes more powerful by using `interfaces`.

### CLASS IMPLEMENTING INTERFACE:

In TypeScript, a class can implement interfaces to enforce particular contracts (similar to languages such as Java and C#).

To implement an interface inside a class, we use the `implements` (with s) keyword after the class name and then specify the interface name. Check out the following code:

```typescript
interface CourseInterface {
  name: string;
  price: number;
  onPurchase: () => void;
}

class Course implements CourseInterface {
  name: string;
  price: number;

  constructor(name: string, price: number) {
    this.name = name;
    this.price = price;
  }

  onPurchase() {
    console.log(`You have purchased ${this.name}`);
  }
}
```

As you can see, there is an interface named `CourseInterface` that has three properties, which are `name` of type `string`, `price` of type `number`, and `onPurchase`, a function that accepts no parameters and returns `void`.

There is also a class named `Course` that implements `CourseInterface`. Thus, it is necessary for a `Course` class to implement the same **mandatory properties and methods** that the `CourseInterface` possesses. If we miss any one of these **mandatory properties**, we will receive the following compilation error:

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

The error reads, `Property 'onPurchase' is missing in type 'Course', but it is required in type 'CourseInterface'`. Therefore, we must either add the `onPurchase` method or make that property optional.

> The **interface** is somewhat similar to the **abstract class**, but there are some differences. We'll discuss the **difference between an abstract class and an interface** in an upcoming post here.

### IMPLEMENTING MULTIPLE INTERFACES:

Classes can implement multiple interfaces at once.

Let's take an example to understand this:

```typescript
interface Shape {
  getArea(): number;
}

interface Color {
  color: string;
  getColor(): string;
}

class Circle implements Shape, Color {
  private radius: number;
  color: string;

  constructor(rad: number, clr: string) {
    this.radius = rad;
    this.color = clr;
  }

  getArea(): number {
    return Math.PI * this.radius * this.radius;
  }

  getColor(): string {
    return this.color;
  }
}

const circle = new Circle(5, "red");
console.log(circle.getArea()); // prints 78.5398
console.log(circle.getColor()); // prints red
```

The example above shows two interfaces named `Shape` and `Color`, each of which has certain properties with their types. Additionally, we have a class called `Circle` which implements these two interfaces.

If any one of the **mandatory properties** of any one of the interfaces is missing, we will get the following compilation error:

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

These interfaces can also be used to implement other classes as follows:

```typescript
interface Shape {
  getArea(): number;
}

interface Color {
  color: string;
  getColor(): string;
}

class Rectangle implements Shape, Color {
  private length: number;
  private width: number;
  color: string;

  constructor(l: number, w: number, clr: string) {
    this.length = l;
    this.width = w;
    this.color = clr;
  }

  getArea(): number {
    return this.length * this.width;
  }

  getColor(): string {
    return this.color;
  }
}

const rect = new Rectangle(10, 20, "green");
console.log(rect.getArea()); // prints 200
console.log(rect.getColor()); // prints green
```

This allows us to add strict type checks and provide documentation in our code, which makes the development process more convenient and reliable.

## INTERFACE - INTERFACE INHERITANCE:

Extending an `interface` essentially means inheriting it from another `interface`. Similar to class inheritance we can use the `extends` keyword to inherit an interface.

### EXTENDING AN INTERFACE:

Take a look at the following code:

```typescript
interface Animal {
  name: string;
  age: number;
  eat: () => void;
}

interface Dog extends Animal {
  breed: string;
  bark: () => void;
}
```

The above code contains an interface called `Animal` and an interface called `Dog` which inherits `Animal`.

As a result, you can use an interface `Dog` to implement a class that must implement `Dog` as well as the **mandatory properties/methods** of the `Animal` interface.

Take a look at the following code:

```typescript
// example for interface inheritance

interface Animal {
  name: string;
  age: number;
  eat: () => void;
}

interface Dog extends Animal {
  breed: string;
  bark: () => void;
}

class Labrador implements Dog {
  name: string;
  age: number;
  breed: string;

  constructor(name: string, age: number, breed: string) {
    this.name = name;
    this.age = age;
    this.breed = breed;
  }
  bark() {
    console.log("Woof! Woof!");
  }
  eat() {
    console.log("Yum yum");
  }
}

const labrador = new Labrador("Max", 3, "Labrador");
```

You will get a compilation error if you miss any of the `Animal` or `Dog` properties as follows:

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

In this case, we are missing the `eat` property, which is of type `() => void` in the `Animal` interface.

Also, `Animal` can still be used as a valid interface independently if required.

### EXTENDING MULTIPLE INTERFACES:

It is possible to extend more than one interface while inheriting. Here is an example:

```typescript
interface Species {
  family: string;
  genus: string;
}

interface Animal {
  name: string;
  age: number;
  eat: () => void;
}

interface Dog extends Animal, Species {
  breed: string;
  bark: () => void;
}
```

In this case, we have another interface called `Species` that is inherited by the `Dog` interface. In any class that implements a `Dog` interface, we now need to add all the **mandatory** **properties** of `Species`, `Animal`, and `Dog`.

## DIFFERENCE BETWEEN A TYPE AND AN INTERFACE:

### USE CASE:

`interfaces` are a way to describe data shapes, such as objects. The `type` attribute defines the type of data, for example, union, primitive, intersection, tuple, any, or even object.

### EXTENDS & IMPLEMENTS:

As we saw earlier in this blog, we can easily extend and implement `interfaces` with TypeScript. However, this is not possible with `types`. **(Except that the type is representing an object)**

For example,

\*\*The following code snippets are valid: \*\*

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

**The following code snippets are invalid:**

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

The error above occurs because `ABC` is a `union type`, not an `object` type

### MERGING

When you create multiple interfaces with the same name and some common and some unique properties, TypeScript will merge all the properties and will **NOT** generate a compilation error as follows:

```typescript
interface Int1 {
  a: number;
  b: number;
  c: number;
}

interface Int1 {
  a: number;
  b: number;
  d: number;
}

// int1 should include a, b, c and d properties
// if you miss any mandatory property, you will get an error
const int1: Int1 = {
  a: 1,
  b: 2,
  c: 3,
  d: 4,
};

// Compiles without an error
```

However, with `types`, TypeScript will throw a compilation error because declaring two `types` with the same name is not allowed. Refer to the following image:

![image.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1645646759482/bqSUF-Eme.png align="left")

## DIFFERENCE BETWEEN AN ABSTRACT CLASS AND AN INTERFACE:

**Interfaces:**

* Promote loose coupling *(class is dependent on an interface rather than a class for the implementation)*
    
* We can implement more than one interface in a class.
    
* Set up a contract between different classes *(the syntax/structure for classes to follow)*
    
* Serve as a “gatekeeper” to a function *(function can enforce the contract)*
    
* Work best when we have very different objects that we want to work with together.
    

**Abstract classes:**

* Strongly couple classes together *(class is dependent on another class)*
    
* We can't inherit more than one abstract class.
    
* Set up a contract between different classes *(the syntax/structure for classes to follow)*
    
* Work best when we are trying to build up the definition/blueprint of an object.
    

## CONCLUSION:

* In this article, we have written multiple TypeScript interfaces to represent various data structures, discovered how we can use different interfaces together as building blocks to create powerful types, and learned about the differences between normal type declarations and interfaces.
    
* When should we use **classes and interfaces**? If you want to create and pass a type-checked class object, you should use TypeScript classes. If you need to work without creating an object, an interface is best for you.
    
* In the last two articles, we opened two useful approaches: blueprints (classes) and contracts (interfaces). You can use both of them together or just one. It is up to you.
    
* You can now start writing interfaces for data structures in your codebase, allowing you to have type-safe code as well as documentation.
    

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)
