Interfaces in TypeScript

Zoe Friedman
3 min readOct 28, 2021

--

What is an Interface?

Interface + Classes are how we get really strong code reuse in TypeScript.

Interfaces allow us to effectively create new types! We are constantly going to be creating functions that accepts arguments that are typed with interfaces. These interfaces are going to become the gatekeepers to our functions. They define the property names and value types of an object and it is then up to our classes to satisfy those requirements if they want to play ball.

Let’s say we’re creating an application with a map. And we’re going to want to show markers representing a lot of different objects on the map:

// Client.tsexport class Client {
name: string;
location: {
lat: number;
lng: number;
}
constructor(){
this.name = name.firstName()
this.location = {
lat: parseFloat(address.latitude()),
lng: parseFloat(address.longitude())
}
}
}

and

// Ghost.tsexport class Ghost {
ghostName: string;
evilPower: string;
location: {
lat: number;
lng: number;
}
constructor(){
this.ghostName = ghost.ghostName()
this.evilPower = ghost.evilPower()
this.location = {
lat: parseFloat(address.latitude()),
lng: parseFloat(address.longitude())
}
}
}

In our Map file, without interfaces, here is how displaying these two objects on the map might look:

// Map.tsexport class Map {...addUserMarker(client: Client): void {
new google.maps.Marker({
map: this.googleMap,
position: {
lat: client.location.lat,
lng: client.location.lng
}
})
}
addCompanyMarker(ghost: Ghost): void {
new google.maps.Marker({
map: this.googleMap,
position: {
lat: ghost.location.lat,
lng: ghost.location.lng
}
})
}
}

TypeScript does have a solution for this where we can pass in both of these objects as options into a more generic function:

addMarker(mappable: Client| Ghost): void {
new google.maps.Marker({
map: this.googleMap,
position: {
lat: mappable.location.lat,
lng: mappable.location.lng
}
})

Above we make a variable called “mappable” and since both the Client and the Ghost objects have the location.lat and location.lng properties, they work here. But imagine as our application expands. What if we wanted to add many different other locations to our map??

addMarker(mappable: Client | Ghost | LandMark | HotDogCart | FireStation | Restaurant | Bathroom | Parking | MovieTheatre ): void {
new google.maps.Marker({
map: this.googleMap,
position: {
lat: mappable.location.lat,
lng: mappable.location.lng
}
})

We also would have to import all of these objects into our file:

import { Client } from "./Client"
import { Ghost} from './Ghost'
import { LandMark } from './LandMark'
import { HotDogCart } from './HotDogCart'
...and on and on..

^^^ This isn’t going to work. Enter, interfaces!

The keymaster.

Interfaces create a type and allows our classes to opt in. Rather than saying our method has to accommodate all of these classes, the classes have to chose to fulfill or satisfy the interface by having the correct properties with the correct names and correct types. Here, have a look. At the top of our file we put our interface:

// Map.tsinterface Mappable {
location: {
lat: number;
lng: number;
}
}
export class Map {...

and then inside of our Map class inside of our addMarker function:

addMarker(mappable: Mappable): void {
new google.maps.Marker({
map: this.googleMap,
position: {
lat: mappable.location.lat,
lng: mappable.location.lng
}
})

Bam! Our Map class now completely stands alone independently and is reusable anywhere. That is another power of TypeScript in action. We can add new classes that will be Mappable to our heart’s content without having to update our Map class.

--

--