Interfaces in TypeScript
Tags: programming, typescript, webdev, beginners
Published: 2022-07-15 19:25:04

## Abstract

An interface allows us to enforce a certain structure for an entity to follow. An interface contains the names and types of all the properties. It also includes the function signature, as well as the type of arguments and return type.

If a class implements an interface, it has to abide by it. That is, that class will have to implement all the properties and methods in that interface along with their types.

An interface acts as a contract with our application. It states what needs to be done but doesn't specify how it will be done. Further we cannot instantiate an interface. It can only be referenced by the class object that implements it.

## Why Interfaces?

Interfaces helps maintain consistency in our code. It adds the functionality of strong type checking for your functions, variables, or the class that is implementing the interface. If an entity fails to properly implement an interface,the compiler will throw an error. This allows us to detect problems before they occur, which is definitely advantageous and simplifies maintenance in the long run.

Further Interfaces helps to improve the performance of JavaScript engines by making just one shape of the type of the interface and each of the objects just stores corresponding values of the properties as defined in the interface.

We should use an interface for the following:

- Validating specific structure of properties
- Objects as parameters
- Objects returned from functions

## Declaring Interfaces

We declare an interface using the _interface_ keyword in a .ts file. The following example shows an example of the syntax:

```javascript
interface Person {
  name: string;
  age: number;
  speak(a: string): void;
  spend(a: number): number;
}
```

Here we've created an interface Person with properties _name_ which is a string and _age_ which is a number. Then, we have two methods namely _speak_ which takes in a parameter of type string and returns void and _spend_ which takes in a parameter of type number and returns number.

Now let us implement the interface:
```javascript
let me: Person = {
	name: 'shaun',
  	age: 30,
  	speak(language: string): void {
    		console.log(text);
  	},
  	spend(amount: number): number {
    		console.log('I spent ', amount);
    		return amount;
  	},
}
```

Here we have created an object _me_ of type Person. Now, the compiler will expect the me object to have all the properties of the Person interface. If even one of the properties is missing or if its value isn't of the same type specified in the interface, the compiler will throw an error.

## Specifying Optional Properties

In the previous code sample, all properties in the interface are required. There are certain cases, however, where we would expect our objects to have properties that are **optional**. We can achieve this by placing a question mark (?) just before the property's type annotation when declaring the interface:
```javascript
interface Post {
  title: string;
  content: string;
  tags?: string[];
}
```

In the above code, we are telling the compiler that it is possible to have Post objects **without tags**.

## Specifying Read-only properties

A read-only property can only be changed when an object is created for the first time. That is, if you attempt to reassign its value, the compiler will generate an error.

We can mark a property as read-only by adding the _readonly_ keyword before a property's name.
```javascript
interface PlacedOrder {
  itemsInOrder: number;
  totalCost: number;
  readonly dateCreated: Date;
}
```

### readonly vs const

The easiest way to remember whether to use readonly or const is to ask whether you're using it on a variable or a property. Variables use const whereas properties use readonly.

## Interface As Functions

TypeScript interfaces can also be used to annotate functions. We can do so by creating a _callable signature_ inside the interface.This is like a function declaration with only the parameter list and return type given.
```javascript
interface SumFunc {
  (a: number, b: number): number;
}

const add: SumFunc = (num1, num2) => {
  return num1 + num2;
}
```

**Apart from this, the function parameters can also be declared as the type of an interface**.

## Interface As Class Types

Interfaces can also be used to properly type classes created in JavaScript. To achieve this, we create an interface that contains the class' properties and methods, then we use the_implements_ keyword when creating our class.
```javascript
interface Person {
  name: string;
  age: number;
speak(a: string): void;
spend(a: number): number;
}

class Me implements Person{
     name: string;
    age: number;
   speak(a: string): void;
   spend(a: number): number;
  private idNumber: number;

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

**Please note, the interface defines only the public properties of a class rather than both the public and private side.**

## Generic Interfaces

You may not always be sure of the type of data that each property in your interface contains. The simplest method would be to simply add _any_ type to such properties. This, however, would make our code less type-safe.

To assist with scenarios like this, TypeScript allows you to create **generic interfaces**. Generic interfaces are defined in the same way as regular interfaces, but they allow you to provide one or more parameters in the interface's description which are further used to specify the type of a property inside the interface.

We can create a generic interface by passing type parameters, using angle brackets(<>), to our interface.
```javascript
interface ApiData<T> {
  payload: T[];
  code: number;	
  date: Date;
}

interface UserData {
  name: string;
  email: string;
}

async function loadDataFromApi() {
 const data: ApiData<UserData> = await fetch('/some/api/endpoint'')
}
```

Here the "ApiData<UserData>" converts to:
```javascript
interface ApiData<T> {
 payload: UserData[];
 code: number;
 date: Date;
}
```


## Extending an Interface

Just like in classes, the interface can also inherit from single or multiple interfaces as well as a class. To extend an interface, we use the _extends_ keyword. We can extend multiple interfaces when they are separated by commas.
```javascript
interface PetsInterface {
  name: string;  
}
interface DogInterface extends PetsInterface {
  breed: string;
}
interface CatInterface extends PetsInterface {
  breed: string;
}
class Cat implements CatInterface {
  name: string = 'Garfield';
  breed: string = 'Tabby';
}
class Dog implements DogInterface {
  name: string = 'Rover';
  breed: string = 'Poodle';
}
```

## Conclusion

- Interfaces defines the structure for entities to follow and they can be implemented by functions or classes. We can define optional properties on an interface using ? and read-only properties by using the readonly keyword in the property name.
- Interfaces can be extended to import properties of other interfaces or classes using the extends keyword.
- We can use generics with interfaces to create reusable components.
- Interfaces also help in improving the performance of JavaScript engines.

## References:
- [Ankita Masand's blog on logrocket](https://blog.logrocket.com/interfaces-in-typescript-what-are-they-and-how-do-we-use-them-befbc69b38b3/)
- [The net ninja's video on Typescript Interfaces](https://www.youtube.com/watch?v=VbW6vWTaHOY)