TypeScript Up & Running: Interfaces

Beyond the basics types that we discussed in the previous article, Typescript allow us to create custom types that describe the data structure and the behavior of our code.

Typescript provide three ways for custom types: Interfaces, Enums and Classes. In this article we’ll focus on Interfaces, how to create and use them.

If you are familiar with C# or Java, you may have interfered with Interfaces, in Typescript it’s pretty much the same, however don’t worry if this concept is new for you, we’ll explain it with details.


The Problem

let’s start with this product Object

var product = {
id: 1,
name: ‘A blue sofa’,
inStock: true,
price: 12.50,
tags: [‘home’, ‘furniture’, ‘sofa’]
}

We want product to follow a particular data structure, well we can add type for each property while declaring the variable like this.

var product : {
id: number,
name: string,
inStock: boolean,
price: number,
tags: string[]
} = {
id: 1,
name: 'A blue sofa',
inStock: true,
price: 12.50,
tags: ['home', 'furniture', 'sofa']
}
Notice how the syntax is not clear enough.

Now what if we want to declare another product, or even a list of products, it’s not right to retype the same types mapping for each time we create a new variable of the same structure!

Interfaces

Well this where interfaces come handy, it allow us to write the data structure once, and use it with each variable have the same shape.

we can write product interface simply like this

interface ProductType {
id: number,
name: string,
inStock: boolean,
price: number,
tags: string[]
}
Notice how the ProductType interface contain all product properties with their corresponding types

Now we can use ProductType interface like this

var product: ProductType = {
id: 1,
name: ‘A blue sofa’,
inStock: true,
price: 12.50,
tags: [‘home’, ‘furniture’, ‘sofa’]
}

Or like this

var product: ProductType
product = {
id: 1,
name: 'A blue sofa',
inStock: true,
price: 12.50,
tags: ['home', 'furniture', 'sofa']
}

Or if we want to create a product list

interface ProductType {
id: number,
name: string,
inStock: boolean,
price: number,
tags: string[]
}
var product = {
id: 1,
name: 'A blue sofa',
inStock: true,
price: 12.50,
tags: ['home', 'furniture', 'sofa']
}
var productList : Array<ProductType> // or we can use ProductType[]
productList[0] = product

Both Array<ProductType> and ProductType[] means that we need an array of ProductType, it’s up to you which syntax to use.

Notice that in those examples we strictly followed the ProductType interface data structure with the right type, no missing property, no additional one, because in both case it will cause an error.
Typescript notify us about the missing ‘id’ Property
Typescript notify us about the ‘category’ Property, which doesn’t exist in ProductType Interface

Optional Properties

What if we need to add a category field to some products, if we add the category attribute to the ProductType interface, then it’ll be mandatory for each product, but we just want it for some product!

Well if we follow the category property name with question mark ‘?’, Typescript will allow products to contain the optional category field for the needed ones.

interface ProductType {
id: number,
name: string,
inStock: boolean,
price: number,
tags: string[],
category?: string
}
Remember, Typescript accept the creation of product with missing category since it’s optional, but once we add the category, it must follow the same type specified in the ProductType Interface.

Index Signature

Sometime we need to add metadata for products, but the metadata for a furniture product (weight, height, width, etc) are not the same for an alimental product (calories, fabrication date, etc). Sure we can add all those metadata fields as optional, but i just named two products categories, imagine if we have more than 10 categories, it will be a huge Interface.

In this case when we want to add different extra properties that are used in different ways, Typescript offer Index Signature which is a handy way to handle extra properties, we can adjust the ProductType Interface to look like this,

interface ProductType {
id: number,
name: string,
inStock: boolean,
price: number,
tags: string[],
category?: string,
[propName: string]: any
}
Notice how propName surrounded by brackets ‘[]’, it’s ES6 computed property.
Note that Index Signature parameter must be either ‘string’, or ‘number’ .
Remember, ‘any’ allow the Index Signature property to accept all data type.

Now we can add as much metadata as we want, varies depending upon product category.

var productList : ProductType[]
productList[0] = {
id: 1,
name: 'A blue sofa',
category: 'furniture',
inStock: true,
price: 12.50,
tags: ['home', 'furniture', 'sofa'],
weight: 45,
width: 150,
height: 80
}
productList[1] = {
id: 2,
name: 'bazinga soft drink',
category: 'alimental',
inStock: true,
price: 5.50,
tags: ['drinks'],
calories: 250,
fabricationDate: '09/08/2016'
}
Notice that the Index Signature accept all extra properties as long as the property name isn’t predefined in the Interface.

Since category property is defined in the Interface as a string, it must be a string, the ‘any’ type will be associated only to the undefined properties

Remember, the predefined properties has priority over the Index Signature ones

Usage with functions

For in depth usage lets say we want a function that add new product to the productList, it will accept the current productList and the new product, and it will return the new productList

function addProduct(productList:ProductType[], product:ProductType): ProductType[] 
{
return productList.concat(product)
}
Notice, we used ProductType[] to represent the product array, like we said before we can also use Array<ProductType>.
var productList : Array<ProductType> = []
productList = addProduct(productList, {
id: 1,
description : 'A blue sofa',
inStock: true,
price: 12.50,
tags: ['home', 'furniture', 'sofa'],
})
Notice, how we made a mistake, we changed the name property with description, in vanilla Javascript probably we will not notice this error until we run the code!

This is were we benefit from TypeScript static type checking, Typescript will notify us about the missing property while we code.

Notice that Typescript didn’t say that the description property is wrong.

Since we used Index Signature, Typescript will accept description property as an extra data, but it will notify us about the missing name property, since it’s a mandatory field in the ProductType Interface.

Now, addProduct seems to work fine, since the product argument is conforming with the ProductType Interface, and here we squashed a bug even before running the code, thanks to Typescript.


Enough For Now, the next article of this series will be about Enums.

If you liked this, click the 💚 below so other people benefit

My Twitter: @mouafa