Start with ECMAScript 2015 also known as ECMAScript 6, JavaScript has provided the ability to use this object-oriented class-base approach.
for full description read Classes in typescript.
simple class based example
class Passenger {
private readonly _firstName: string;
private readonly _lastName: string;
constructor(fName: string, lName: string) {
this._firstName = fName;
this._lastName = lName;
}
get fullName(): string {
return this._firstName + this._lastName;
}
}
As you can see classes can contain data members and member functions (fields and methods).
And with this class we can now create instance of our passenger every where we need to, like so.
let passenger= new Passenger('Anna', 'Brown');
Now, let's say we want to make a contract inside our application to shape and force all classes to have some specific fields or maybe methods, maybe we need have them to be able to use that items inside some select component ! 🤷♂️
Here's Interfaces can help us, as "One of TypeScript’s core principles is that type checking focuses on the shape that values have", you can read full description about Interfaces here.
Select able interface
interface SelectableElement {
id: number;
title: string;
}
Now we have contract that can be implemented to our classes those we want to display their contents inside our select component 👏
Let's implement SelectableElement
to our Passenger
class, and make it more usable inside our application 😎
class Passenger implements SelectableElement {
id: number;
private readonly _firstName: string;
private readonly _lastName: string;
constructor(fName: string, lName: string, id?: number) {
this._firstName = fName;
this._lastName = lName;
this.id = id ?? Math.floor(Math.random() * 1000);
}
get fullName(): string {
return this._firstName + this._lastName;
}
get title(): string {
return this.fullName;
}
}
The most beautiful part is that immediately after implementing the our interface into the class, TypeScript will yell error and say we need to implement those members.
Another example will be when we will try to create Resolver inside our Angular application, as we need to implement Resolve
or when we implementing LifeCycles
to our components.
This was one of the benefits, the second one will be when we want to have contract when data is moving inside our application from service to service, or even when we want to send data to our Server Side Application or retrieving them from other web services !
One of practices that I truly ❤ is to have models inside our Front-End application independent of Back-End models ( or better to say Schema
👀 ), and then map them B4 sending to and after receiving them from our API ✌
Let's create Passenger
interface
interface IPassenger {
firstName: string;
lastName: string;
}
Can you see it ?? 🧐
Yes.. We can implement our interface to the class too !
class Passenger implements SelectableElement, IPassenger {
id: number;
firstName: string;
lastName: string;
constructor(fName: string, lName: string, id?: number) {
this.firstName = fName;
this.lastName = lName;
this.id = id ?? Math.floor(Math.random() * 1000);
}
get fullName(): string {
return this.firstName + this.lastName;
}
get title(): string {
return this.fullName;
}
}
As you can see one of disadvantages will be that we can not have those two fields private any more, as we can not introduce private members to interfaces, maybe some day we can ! 🙄
Now let's see how we can implement some methods to our Passenger
class to map data b4 send them to API 🥱
But first for better read ability it can be good practice to introduce our API Schemas separately, like so
interface IPassengerSchema {
identifier: number;
lName: string;
fName: string;
}
Okay, now we are ready to implement methods for mapping passengers data 🎨
Map to Schema class Passenger
...
get mapToApiSchema(): IPassengerSchema {
return {
identifier: this.id,
fName: this.firstName,
lName: this.lastName
}
}
...
From now on we can use this method when ever we want to send passenger data to our Back-End, like so
const passenger = new Passenger('Anna', 'Brown');
const pDto = passenger.mapToApiSchema;
Map from schema class Passenger
...
static getMapFromApiSchema(passenger: IPassengerSchema): IPassenger {
return new Passenger(passenger.fName, passenger.lName, passenger.identifier);
}
...
Now we can use this method like this
const passenger = Passenger.getMapFromApiSchema(passengerFromApi);
Conclusion
Classes and Interface are both powerful and useful 🤔
Stop putting them versus each other and start using them together 🤐
Classes are rich objects that can contain everything that we expect from model ☺
Interfaces are contracts that will help us to keep our application data shape, the way we want 😏
Top comments (0)