Dealing with Classes and Interfaces

Profile picture of Bhavik Bamania
Bhavik Bamania
·7 min read
Dealing with classes and interfaces
Dealing with classes and interfaces

Even though Javascript is an object-based, not an object-oriented programming language, still has ways of using object-oriented programming. This makes Typescript extend its support to classes based javascript code as well, in this article we will walk through how we can use Typescript with Javascript’s classes and will learn something new called “interfaces”.

Prerequisite

If you are going sequentially with the tutorial, then you must be aware of things like, what is typescript, how to configure typescript from scratch, what are types in typescript, and how we can use types provided by typescript.

If you haven’t gone through these topics and they seem like weird creatures to you, don’t worry here you can find these articles:

 

Getting Started

Since this article will contain a few demos the first thing we will need to do is a minimal typescript code setup, for this, you can refer to the first article where you can see how we can set up a minimal typescript project under “Getting started with Typescript”, for the future article as well we’ll follow the same code setup.

So, I hope now what you have is a brand new blank project with Typescript configured, which means we are ready to hit the keyboard.

Classes

Being a Javascript developer, you should be aware in Javascript we can write classes, Javascript classes are introduced in ES6. In Javascript, they are used as a template for Javascript objects.

In Typescript, the members of a class ie., methods and properties are typed using type annotations, similar to variables. For instance:

class Person {
  name: string;
}

const person = new Person();
person.name = "Pyramid Oscar";

In the above example, you can see that the property of the Person class name expects a string value, and the typescript checks whether the name property receives a string or not.

Interface

An interface is used to describe the structure of a class only not the concrete values of a class or in other words of objects because classes are nothing but the syntactic sugar coat of an object, the interfaces are introduced by Typescript and Javascript doesn’t understand them. A typical interface can be defined as follows:

interface Person {
   name: string; 
   age:number;
}

One very important thing to note here is, that there are no such restrictions to start an interface name with capital letters, but since most of the libraries follow this practice therefore it is good practice to follow the standards set by libraries.

Let’s create one demo using the interface to understand it in a better way.

interface Person {
    name: string;
    age: number;
    greet(phrase: string): void;
}

let user1: Person;

user1 = {
    name: "Pyramid Oscar",
    age: 28,
    greet(phrase: string) {
        console.log(`${phrase} ${this.name}`)
    }
}

user1.greet("Hello World, I am")

Nothing fancy here, just a basic interface that expects three properties, name of string type, age of number type, and a function greet of void type. Later this interface is implemented by the user1 object.

Using Interfaces with Classes

In Javascript, classes are nothing but a syntactic sugarcoat of an object, therefore this means that we can implement the interface on a class as well but before we dig deep into this let’s know why we need an interface.

Why do we need the Interface?

The reason for using an interface with objects and classes is simple, custom types are more flexible while the interface is strict and the defined structure will be used to define an object. They can be handy while working with classes as the interface can be shared across various classes.

interface Greetable {
    name: string;
    greet(phrase: string): void
}

class Person implements Greetable {
    name: string;


    constructor(n: string) {
        this.name= n;
    }

    greet(phrase: string): void {
        console.log(`${phrase} ${this.name}`)
    }
}

let user : Greetable;
user = new Person("Pyramid Oscar")
user.greet("Hello World, I am ");
console.log("user",user);

Not only this we can implement multiple interfaces in a class, unlike inheritance. For example:

interface Greetable {
    name: string;
}
interface Greetable {
    age: number
}
class Person implements Greetable, Person{
  name: string;
  age: number;
}

const person = new Person();
person.name = "Pyramid Oscar";
person.age = 30;
person.job = "Author";

You can add more fields than what the interface is asking to add but whatever is defined in the interface must be added to the class that has implemented the interface, as given in the above example.

You can use the interface on some constant and variable which will store the other class which is based on the interface type because it implements it.

Readonly Property in Interface

Just like we can add readonly property to a class in Javascript to avoid modifying the values of the property of that class. We can also add a read-only property in the interface.

It will allow you to set the property only once and can’t be changed after object initialization. Just like this:

interface Greetable {
    readonly name: string;
}

Extending Interfaces

If you like to implement inheritance on an interface, well then you have that flexibility, we can implement inheritance in an interface by using the extends keyword.

We can then use the interface just like we use class allowing them to perform their specific job. 

However, the functionality of inheritance will be similar to the class but there is one striking difference in the case of interfaces. You can extend more than one interface by just adding it with comma separated whereas in class we can’t inherit multiple classes. We can do this because we have an interface only in Typescript not in Javascript so we can break Javascript rules by inheriting multiple interfaces and merging them into one.

Let’s see a quick demo of inheritance.

interface Named {
  readonly name: string;
  age?: number;
}

interface Greetable extends Named {
  greet(phrase: string): void;
}

class PersonData implements Greetable {
  name: string;
  age?: number;

  constructor(n: string, age?: number) {
    this.name = n;
    if (age) {
      this.age = age;
    }
  }

  greet(phrase: string): void {
    if (this.age) {
      console.log(`${phrase} ${this.name} and my age is ${this.age}`);
    }
  }
}

let user2: Greetable;
user2 = new PersonData("Pyramid Oscar");
user2.greet("Hello World, I am ");
console.log("user", user);

Along with inheritance, I would like to cover one more thing in the interface if you want to add any optional parameters to an interface we can add them using “?” as you can see in the above example the age property is optional.

Not only a property but you can also define an optional method just like any normal object property. Similar to this we can have optional properties in class as well. We can also provide a fallback value or just pass “?” in the constructor for an optional parameter in a class. However, here we need to keep one thing in mind they are loosely related, we need to make sure that we do not get any error in case of a mismatch with an interface or with a constructor.

Interface as a Function

Since functions are just objects, therefore, we can create interfaces for the function types as well. However, using type for functional types is a better and recommended approach but as an alternative, we can use interface as well. Let’s take this as an example

interface AddFn {
    (a: number, b: number): number;
}

let additionVar : AddFn;

additionVar = (n1: number, n2: number) => {
    return n1 + n2;
}

Optional Parameters and properties in Interface and Classes

Just like we define optional properties in an object or parameters in a function we can also define optional parameters in an interface using “?”.

interface Named {
  name?: string;
  address?: string;
}

Similarly, we can do the same thing with methods as well, like this:

interface Named {
  name?: string;
  address?: string;
  displayName?: string;
}

That’s something cool, isn’t it? Similarly, we can also set optional parameters and properties to a class and its constructor

class Person implements Printer {
  name?: string;
  age = 30;

  constructor(n?: string) {
    if (n) {
      this.name = n;
    }
  }

  display() {
    if (this.name) {
      console.log('Your name is: ' + this.name);
    } else {
      console.log('Hello World!');
    }
  }
}

Conclusion

And, that’s it, in this article we have covered how we can leverage typescript when we are working with classes and interfaces. If you are reading this I hope that you’ll be able to work with interfaces which is a new concept in terms of typescript. In the upcoming blog, we will be working with some advanced typescript concepts.

Author of Bhavik Bamania

Written by

Bhavik Bamania