Interface + Classes = How we get strong code reuse
Long Type Annotation
Given an object like so:
1 2 3 4 5 |
const oldCivic = { model: 2000, name: 'civic', broken: true }; |
Say we want to implement a function that takes in such a type. We say, this parameter is called vehicle, and it takes in an object with property name of type string, year of type number, and whether it is broken…which is of type boolean.
1 2 3 4 5 6 |
const printVehicle = (vehicle: { name: string, year: number, broken: boolean }): void => { console.log(`Name: ${vehicle.name}`); console.log(`Year: ${vehicle.year}`); console.log(`Broken? ${vehicle.broken}`); } pintVehicle(oldCivic); |
Boy, this function annotation is really really long and tiring on the eyes
How to fix this long Annotation?
Creating an interface is like we are creating a type.
1 2 3 4 5 |
const oldCivic = { name: 'civic', year: 2000, broken: true } |
will tell us that we need a type like this:
1 2 3 4 5 |
interface Vehicle { name: string; year: number; broken: boolean; } |
now we can do this:
1 2 3 4 5 |
const printVehicle = (vehicle:Vehicle): void => { console.log(vehicle); } printVehicle(oldCivic); |
which is much easier on the eyes and fingers.
By definition, typescript gives us compile time checking. If we change true to 1 in oldCivic, we’ll get compile time checking for oldCivic, that it does not conform to the interface.
Functions around Interfaces
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
const oldCivic = { name: 'civic', year: 2000, broken: true, summary(): string { return 'awesome car!' } } interface Vehicle { name: string; year: number; broken: boolean; summary():string; } |
Is it important that Vehicles MUST have these properties? No, as long as Vehicle interface is a subset of what is provided in oldCivic, then we are okay.
In other words, as long as what we declared are included in the interface, then it is ok.
That is the ONLY QUESTION that is asked when we’re trying to check if the passed in object has the properties of the interface.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
const oldCivic = { name: 'civic', year: 2000, broken: true, summary(): string { return 'awesome car!' } } interface Vehicle { summary():string; } const printVehicle = (vehicle:Vehicle): void => { console.log(vehicle); console.log(vehicle.summary()); } printVehicle(oldCivic); |
Let’s change the name Vehicle to Reportable. It is much more descriptive.
And change the function name.
1 2 3 4 5 6 7 |
interface Reportable { summary(): string; } const printSummary = (item: Reportable): void => { console.log(item.summary()); } |
Code Reuse with Interfaces
1 2 3 4 5 6 7 8 |
const drink = { color: 'brown', carbonated: true, sugar: 40, summary(): string { return 'My drink has ${this.sugar} grams of sugar`; } } |
Notice we both have summary function that returns a string. They are both considered to be ‘Reportable’ types. So they can both be used in the parameter.
1 2 3 |
const printSummary = (item: Reportable): void => { console.log(item.summary()); } |
We can use a single interface to describe very different objects.