100 days of TypeScript (Day 4)

Wow. Day 4 already 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); // Wait, why can I see i here?

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.

100 Days of TypeScript (Day 1)

Introducing TypeScript

It’s been a while since I’ve had the chance to write in this blog and I really wanted to come up with something different for me. If you’re a long time follower of mine, you probably know that I wrote a book on TypeScript a couple of years ago, so I thought it would be fun for me to embark on one of those 100 Days of types of blog and write a series introducing you to TypeScript if you’ve not used it before; 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.

Note: This blog series isn’t going to be a continuous 100 days of posting, it’s 100 days by the end.

So, what is TypeScript? If you go to the TypeScript website, the tagline is “TypeScript is JavaScript with syntax for types”. What does that actually mean?

TypeScript was originally built out of the idea that writing high quality JavaScript was difficult. While that’s a subjective opinion, TypeScript rapidly evolved, giving us the ability to use up and coming JavaScript features via polyfills (don’t worry about this, we’ll come back to this in a future post). TypeScript now ranks as one of the most popular languages around.

Getting Started

Okay, you can develop TypeScript apps online in the playground, but I really recommend installing it locally. To do that, you’re going to need to use something like npm, yarn or pnmpm. If you don’t have npm installed, you need to install Node to get it. Assuming you have installed Node, you can install TypeScript with the following command (depends on the package manager you’re using).

npm install -g typescript

It’s time to write our first TypeScript application (all code is available in github).

This is going to be a simple program to add two numbers together. I’m going to use the TypeScript compiler to set some things up ready for when I want to compile my TypeScript code.

tsc --init

This has created a file called tsconfig.json which sets up compiler options that determine what our JavaScript will look like when we’ve compiled it from TypeScript; oh wait, didn’t I mention that TypeScript compiles down to JavaScript? A quick note – I pretty much always use Visual Studio Code for editing my TypeScript; it’s a great choice if you’ve not used it before.

I’m going to add a file called day1.ts to add my TypeScript to add my first piece example. The .ts extension tells us that this is a TypeScript file. If you have used JavaScript before, the code will look familiar to you. This is what our add function would look like as a JavaScript method.

function add(number1, number2) {
    return number1 + number2;
}

I said that I wanted the add function to be able to add two numbers. I don’t want it to add two strings together or a date and a string. This is where the first strength of TypeScript really comes into its own, and where the TypeScript tagline makes sense. I am going to constrain the function to accept numbers and return a number using the following syntax.

function add(number1: number, number2: number): number {
    return number1 + number2;
}

If I attempt to pass something that’s not a number to either of the parameters, I won’t be able to compile the code. I can’t pass “special” values such as undefined or null to either of the parameters. In other words, I have just written something that will protect me from myself.

In order to test my code, I’m going to call the function and pass the output to the console window like this.

console.log(add(10, 20));

After saving the file, I want to “compile” the TypeScript code so that it turns into JavaScript. To do this, I simply run the tsc command with no parameters. This picks up the contents of the tsconfig.json file and uses that to control how the file is compiled. (The tsc command here should be run in the same directory as the tsconfig.json file).

Now that I have compiled my code, I can run it using the following command.

node day1.js

And that’s it; that’s the first TypeScript program. In day 2, we are going to look at how to change our function over so that it sits inside a class; which will let us see how we can start to leverage object oriented concepts to build reusable blocks.