This is Part 4 of a 100 part series introducing you to TypeScript if you’ve not used it before; which will give you a brush up on things you already know if you’ve used it, and maybe introduce you to some new things that you might not have come across before. In this part, you will learn a little about interfaces in TypeScript.
Wow. Day 4 and we have already covered a lot of material in our quest to go from zero to, well not something cliched, with TypeScript. In the last couple of posts, we delved into using classes in TypeScript so, in this post, I would like to take a bit of a diversion and introduce you to interfaces. Now, if you have come from a language such as C# or Java, you probably think that you won’t learn anything new about interfaces here but interfaces in TypeScript are really cool.
Interfaces as Data
One of the things you probably noticed when we were looking at classes is that they can have behaviour. In other words, they aren’t just about data, they also give you the ability to add functionality to manipulate the data. That is incredibly useful but sometimes we want the ability to create something to represent just the data itself. We want to be able to create a type-safe representation of some useful piece of data. You have probably jumped ahead already and thought “I bet Pete’s going to say that interfaces can solve this for me”, and you would be right.
For this post, we are going to create something that represents an email message. We will be able to add recipients, a subject, and the message itself. I am going to start off by writing an interface to represent a single recipient. To create an interface, I change the class
keyword for interface
so my recipient will look just like this.
interface Recipient {
email: string;
}
If I wanted to create an instance of a recipient, I could do something like this.
const recipient: Recipient = { email: 'peter@peter.com' };
Variable Declarations
As a side note, you will have seen that I have been declaring variables using the const
keyword but I have not actually explained where it comes from or what it means. When I started talking about TypeScript, I briefly covered that it was developed to compile to JavaScript. JavaScript has three ways of declaring variables, var
, let
and const
. Originally, JavaScript only had one way, using var
, but this was highly problematic. A little while back, let
and const
were introduced to be a better, less troublesome form of declaration.
Let’s take a look at why var
is such an issue. The issue is down to something called block scope. Block scope refers to where a variable can be seen – it should only be visible in the block that it is being declared in so it would probably come as a surprise that the following bit of code lets me see data that it shouldn’t.
for (var i = 0; i < 10; i++) {
console.log(i);
}
console.log(i);
What we are seeing in this code is var
keyword not being covered by the block scope. This can lead to unfortunate side effects in more complicated code because the value becomes unpredictable.
Enter our heroes, let
and const
.
These were introduced to help us declare variables that respect block scope. We have two keywords because let
allows us to declare a variable and then change the value later on, whereas const
allows us to declare the variable but it cannot be changed.
Back to Our Interface
I have created a simple recipient interface and now I am ready to add one that covers the email itself. The email interface will consist of To, CC and BCC recipients lists, as well as the Subject and Message. If we think about things before we start writing code, we make our lives a lot easier so I am going to ensure that the person using the email interface can choose which of the recipients they want to add. As we have a strong type for our recipient, I am going to use a little TypeScript trick and say that the recipients can receive an array of recipients or the recipient can be null
using | null
.
interface Email {
To: Recipient[] | null;
CC: Recipient[] | null;
BCC: Recipient[] | null;
Subject: string;
Message: string;
}
The syntax of Recipient[] | null
reads that I want an array of Recipient
items OR I want it to be null
.
Now that I have my interface, I am going to create a simple function that accepts an Email and write it to the console.
function sendMessage(message: Email) {
console.log(message);
}
sendMessage(email);
With all the bits and pieces discussed above, you will probably guess that the interface is going to be populated using the const
keyword, just like this (this has to go before the sendMessage(email);
line).
const email: Email = {
To: [{
email: 'bob@bob.com'
}],
CC: [{
email: 'terry@terry.com'
}, {
email: 'chris@chris.com'
}],
BCC: null,
Subject: 'This is my email',
Message: 'This is the message inside my email'
};
Notice that I still had to add the BCC. Without this part, the “shape” of the object would not match the interface and TypeScript is really good at catching things like that.
A quick note about adding individual items to an array. In the recipient entries, each recipient was surrounded by { }
. This is how we add an individual entry into the array, so adding multiple ones is simply a matter of separating these { }
pairs with a comma.
We have reached the end of our introduction to interfaces. They can do so much more so, in the next post, I am going to show how classes and interfaces work together.
The code for this post can be found on github.