Classes and interfaces are powerful structures that facilitate not just object-oriented programming but also type-checking in TypeScript. A class is a blueprint from which we can create objects that share the same configuration – properties and methods. An interface is a group of related properties and methods that describe an object, but neither provides implementation nor initialisation for them.

Since both of these structures define what an object looks like, both can be used in TypeScript to type our variables. The decision to use a class or an interface truly depends on our use case: type-checking only ? implementation details (typically via creating a new instance)? or even both!

We can use classes for type-checking and the underlying implementation – whereas we cannot with an interface. Understanding what we can get from each structure will easily let us make the best decision that will enhance our code and improve our developer experience.

Using TypeScript class

ES6 introduced class officially to the JavaScript ecosystem. TypeScript boosts JavaScript classes with extra power such as type-checking and static properties. This also means that whenever we transpile our code to whatever target JavaScript of our choice, the transpiler will keep all of our class code present in the transpiled file.

Let’s look at an example of defining a class named PizzaMaker:

PizzaMaker is a simple class. It has a static method called create. What makes this method special is that we can use it without creating an instance of the class. We just invoke the method on the class directly :

Then, PizzaMaker.create() returns a new object – not a class – with a name and toppings properties defined from the object passed to it as argument.

If PizzaMaker did not define create as a static method, then to use the method we would need to create an instance of PizzaMaker:

We get the same output we had with create as a static method. Being able to use TypeScript classes with and without an existing instance of a class makes them extremely versatile and flexible. Adding static properties and methods to a class makes them act like a singleton while defining non-static properties and methods make them act like a factory.

Now, unique to TypeScript is the ability to use classes for type-checking. Let’s declare a class that defines what a Pizza looks like:

In the Pizza class definition, we are using a handy TypeScript shorthand to define class properties from the arguments of the constructor – it saves a lot of typing! Pizza can create objects that have a name and a toppings property:

Therefore, we can use the Pizza class to type-check the event argument of PizzaMaker.create(...):

We’ve made PizzaMaker much more declarative, and hence, much more readable. Not only that, but if we need to enforce the same object structure defined in Pizza in other places, we now have a portable construct to do so! Just append export to the definition of Pizza and you get access to it from anywhere in your application.

export class Pizza {
  constructor(public name: string, public toppings: string[]) {}
}

Using Pizza as a class is great if we want to define and create a Pizza, but what if we only want to define the structure of a Pizza but we’d never need to instantiate it? That’s when interface comes handy!

Using TypeScript interface

Unlike classes, an interface is a virtual structure that only exists within the context of TypeScript. The TypeScript compiler uses interfaces solely for type-checking purposes. Once your code is transpiled to its target language, it will be stripped from its interfaces – JavaScript isn’t typed, there’s no use for them there. Also we will use interface only on development cycle and for testing purpose .

While a class may define a factory or a singleton by providing initialisation to its properties and implementation to its methods, an interface is simply a structural contract that defines what the properties of an object should have as a name and as a type. How you implement or initialise the properties declared within the interface is not relevant to it. Let’s see an example by transforming our Pizza class into a Pizza interface:

 our current code provides type-checking for Pizza but can’t create a pizza. For doing that, we have to first initialise an object of type interface. Then we can use this object with the static function create() of the class PizzaMaker.

By Shabazz

Software Engineer, MCSD, Web developer & Angular specialist

Leave a Reply

Your email address will not be published. Required fields are marked *