Interface in typescript explanation with example

Introduction:

Interface defines the syntax that a class must follow. Inside an interface, we can declare different members like properties and functions. Any class that is derived from an interface, must define these members.

We can put common structure inside an interface and different classes or objects can derive from that.

Let me show you how it looks :

interface IStudent {
    name: String,
    age: Number,
    printDetails: () => String
}

This is an interface. It defines one String, one Number and one method. We can create object or class of this interface type.

Example of interface with object:

Let’s try to use the above interface with an object:

interface IStudent {
    name: String,
    age: Number,
    printDetails: () => String
}

var alex: IStudent = { name: "Alex", age: 20, printDetails: (): String => { return `Details for Alex` } }

console.log(alex.name);
console.log(alex.age);
console.log(alex.printDetails());

Here, we are using the same interface we have defined above and we are using that interface with the object alex. As you can see that, we are assigning value for the variables we have defined in the interface and we are implementing the function printDetails.

If you compile this typescript file, it generates the below javascript program:

var alex = { name: "Alex", age: 20, printDetails: function () { return "Details for Alex"; } };
console.log(alex.name);
console.log(alex.age);
console.log(alex.printDetails());

Interface is not here. But the JavaScript program creates the alex object with implementation of the interface.

Output of the program:

Alex
20
Details for Alex

Using interface with a function:

We can also have one argument of type interface in a function. For example:

interface IStudent {
    name: String,
    age: Number
}

function printDetails(student: IStudent) {
    console.log(`Student Name: ${student.name}, Age: ${student.age}`);
}

const alex: IStudent = { name: "Alex", age: 20 };
const bob = { name: "Bob", age: 21 };

printDetails(alex)
printDetails(bob)

Here, printDetails function can accept one parameter of type IStudent, an interface. If you compile the program, it generates the below JavaScript program :

function printDetails(student) {
    console.log("Student Name: " + student.name + ", Age: " + student.age);
}
var alex = { name: "Alex", age: 20 };
var bob = { name: "Bob", age: 21 };
printDetails(alex);
printDetails(bob);

Output :

Student Name: Alex, Age: 20
Student Name: Bob, Age: 21

Now, let’s take a look at the below example :

interface IStudent {
    name: String,
    age: Number
}

function printDetails(student: IStudent) {
    console.log(bob);
    console.log(`Student Name: ${student.name}, Age: ${student.rank}`);
}

const bob = { name: "Bob", age: 21, rank: 2};

printDetails(bob)

It will print :

{ name: 'Bob', age: 21, rank: 2 }
Student Name: Bob, Age: 21

i.e. we have the access to all properties of bob inside printDetails but we can’t access anything that is not defined in IStudent. Here, it will show you one error if you try to access student.rank inside printDetails.

Using single interface with a class:

We can implement one or multiple interfaces with a class. The below example uses one single interface with the class Car:

interface IVehicle {
    name: String,
    doors: Number
}

class Car implements IVehicle {
    name: String = "Audi";
    doors: Number = 4;
    color: String = "Red";

    getDetails(): String {
        return `Name : ${this.name}, Doors: ${this.doors}, Color: ${this.color}`
    }
}

const car: Car = new Car();
console.log(car.name);
console.log(car.doors);
console.log(car.color);
console.log(car.getDetails());

Output :

This program prints the below output:

Audi
4
Red
Name : Audi, Doors: 4, Color: Red

Using multiple interfaces with a class:

Similar to the above example, we can use multiple interfaces with a given class. For example:

interface IVehicle {
    name: String,
    doors: Number,
}

interface IFeatures {
    hasFeatureX: Boolean,
    hasFeatureY: Boolean,
}

class Car implements IVehicle, IFeatures {
    name: String = "Audi";
    doors: Number = 4;
    color: String = "Red";
    hasFeatureX: Boolean = true;
    hasFeatureY: Boolean = false;

    getDetails(): String {
        return `Name : ${this.name}, Doors: ${this.doors}, Color: ${this.color}`
    }
}

const car: Car = new Car();
console.log(car.hasFeatureY);
console.log(car.hasFeatureX);

How to use interface extending interface:

We can have one interface that extends another interface as like below :

interface IVehicle {
    name: String,
    doors: Number,
}

interface IFeatures {
    hasFeatureX: Boolean,
    hasFeatureY: Boolean,
}

interface Audi extends IVehicle, IFeatures {
    model: String,
}

let newCar = {} as Audi;

newCar.model = "X";
newCar.name = "M";
newCar.doors = 4;
newCar.hasFeatureX = true;
newCar.hasFeatureY = false;

interface extending class :

Interface can extend a class. It inherits the member of the class if it extend. Note that it only inherits the members, not their implementations. It can also inherit all private and protected members of a class. If it inherits any private or protected member, that interface can be implemented only by that class or its subclass.

For example:

class Boy {
    name: String;
}

interface IStudent extends Boy {
    printDetails(): void;
}

class Student implements IStudent {
    name: String;

    constructor(name: String) {
        this.name = name;
    }

    printDetails() {
        console.log(this.name);
    }
}

let student = new Student("Alex");

student.printDetails();

In this example, we have a class Student that implements interface IStudent. IStudent extends class Boy. We have to add both name and printDetails in Student class. If you run it, it will print “Alex” as the output.

Optional properties in an interface:

Optional properties are properties that may or maynot have any value. We can have optional properties in an interface. Optional properties are marked with a ?.

interface IStudent{
    name:String;
    age?:Number;
}

let student1: IStudent = {name: "Alex"}
let student2: IStudent = {name: "Bob", age: 20}

Here, age is an optional value. So, we can create objects or class by using or without using age. It will be undefined if we don’t use.

Read only properties in an interface:

read only properties are read only. We can only assign a value to it, we can’t modify it. readonly keyword is used to mark one interface property as read only.

For example:

interface IStudent{
    name:String;
    readonly age:Number;
}

For this interface, age is a readonly property. If you try to reassign any value to this variable, it will throw one error like below:

Using interface as function type:

Using interface, we can also define a function signature. For example:

interface IMsg{
    (name: String, msg: String): void;
};

function morningMsg(name: String, msg: String){
    console.log('Good Morning, '+name+', '+msg);
}

function eveningMsg(name: String, msg: String){
    console.log('Good Evening, '+name+', '+msg);
}

let mMsg: IMsg = morningMsg;
let eMsg: IMsg = eveningMsg;

mMsg('Alex', 'Welcome to our Blog !')
eMsg('Bob', 'Welcome to our Blog !')

It will print the below output:

Good Morning, Alex, Welcome to our Blog !
Good Evening, Bob, Welcome to our Blog !