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:
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:
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:
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.
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.
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.
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.
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" converts to:
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.
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.
Top comments (2)
🤩🔥🔥✨
Sure! Thanks for suggesting :-)