1. Code
  2. TypeScript

TypeScript for Beginners, Part 3: Interfaces

Scroll to top
Read Time: 10 mins
This post is part of a series called TypeScript for Beginners.
TypeScript for Beginners, Part 2: Basic Data Types
TypeScript for Beginners, Part 4: Classes

We began this series with an introductory tutorial that introduced you to different TypeScript features. It also taught you how to install TypeScript and suggested some IDEs that you can use to write and compile your own TypeScript code.

In the second tutorial, we covered different data types available in TypeScript and how using them can help you avoid a lot of errors. Assigning a data type like a string to a particular variable tells TypeScript that you only want to assign a string to it. Relying on this information, TypeScript can point it out to you later when you try to perform an operation that is not supposed to be done on strings.

In this tutorial, you will learn about interfaces in TypeScript. With interfaces, you can go one step further and define the structure or type of more complex objects in your code. Just like simple variable types, these objects will also have to follow a set of rules created by you. This can help you write code more confidently, with less chance of error.

Creating Our First Interface

Let's say you have a lake object in your code, and you use it to store information about some of the largest lakes by area around the world. This lake object will have properties like the name of the lake, its area, length, depth, and the countries in which that lake exists.

The names of the lakes will be stored as a string. The lengths of these lakes will be in kilometers, and the areas will be in square kilometers, but both of these properties will be stored as numbers. The depths of the lakes will be in meters, and this could also be a float.

Since all these lakes are very large, their shorelines are generally not limited to a single country. We will be using an array of strings to store the names of all the countries on the shoreline of a particular lake. A Boolean can be used to specify if the lake is salt water or fresh water. The following code snippet creates an interface for our lake object.

The Lakes interface contains the type of each property that we are going to use while creating our lake objects. If you now try to assign different types of values to any of these properties, you will get an error. Here is an example that stores information about our first lake.

As you can see, the order in which you assign a value to these properties does not matter. However, you cannot omit a value. You will have to assign a value to every property in order to avoid errors while compiling the code.

This way, TypeScript makes sure that you did not miss any of the required values by mistake. Here is an example where we forgot to assign the value of the depth property for a lake.

The screenshot below shows the error message in Visual Studio Code after we forgot to specify the depth. As you can see, the error clearly points out that we are missing the depth property for our lake object.

Missing Property Values in TypeScript InterfaceMissing Property Values in TypeScript InterfaceMissing Property Values in TypeScript Interface

Making Interface Properties Optional

Sometimes, you might need a property only for some specific objects. For example, let's say you want to add a property to specify the months in which a lake is frozen. If you add the property directly to the interface, as we have done until now, you will get an error for other lakes that do not freeze and therefore have no frozen property. Similarly, if you add that property to lakes that are frozen but not in the interface declaration, you will still get an error.

In such cases, you can add a question mark (?) after the name of a property to set it as optional in the interface declaration. This way, you will not get an error for either missing properties or unknown properties. The following example should make it clear.

Using Index Signatures

Optional properties are useful when quite a few of your objects are going to use them. However, what if each lake also had its own unique set of properties like economic activities, the population of different kinds of flora and fauna flourishing in that lake, or the settlements around the lake? Adding so many different properties inside the declaration of the interface itself and making them optional is not ideal.

As a solution, TypeScript allows you to add extra properties to specific objects with the help of index signatures. Adding an index signature to the interface declaration allows you to specify any number of properties for different objects that you are creating. You need to make the following changes to the interface.

In this example, I have used an index signature to add information about different settlements around the lakes. Since each lake will have its own settlements, using optional properties would not have been a good idea.

As another example, let's say you are creating a game with different kinds of enemies. All these enemies will have some common properties like their size and health. These properties can be included in the interface declaration directly. If each category of these enemies has a unique set of weapons, those weapons can be included with the help of an index signature.

Read-Only Properties

When working with different objects, you may need to work with properties that should only be modified when we first create the object. You can mark these properties as readonly in the interface declaration. This is similar to using the const keyword, but const is supposed to be used with variables, while readonly is meant for properties.

TypeScript also allows you to make arrays read-only by using ReadonlyArray<T>. Creating a read-only array will result in the removal of all the mutating methods from it. This is done to make sure that you cannot change the value of individual elements later. Here is an example of using read-only properties in interface declarations.

Functions and Interfaces

You can also use interfaces to describe a function type. This requires you to give the function a call signature with its parameter list and return type. You also need to provide both a name and a type for each of the parameters. Here is an example:

In the above code, we have declared a function interface and used it to define a function that subtracts the damage dealt to a tank from its health. As you can see, you don't have to use the same name for parameters in the interface declaration and the definition for the code to work.

Extending and Combining Interfaces

Interfaces are basically used to describe the type of objects that you want to create in TypeScript using different properties. We can use them to set the structure required to implement other functionality. When working on projects, you will come across situations where two or more objects will share a variety of properties. Defining a new interface for each of those variants will result in a lot of code duplication.

A better alternative here is to extend our previous interface to accommodate more specific properties. Here is an example:

In the above code, we defined three different interfaces to describe points, rectangles, and cuboids. A point only has two properties that determine its x and y coordinates. We extend the Point interface with a Rectangle and add two more properties called width and length. Finally, the Cuboid interface extends Rectangle and adds a height property. It automatically gets all other properties, so it has five different properties.

We also created a variable called cuboid and told TypeScript that it is of type Cuboid. This meant that we were required to specify a value for all five properties. The function volumeCuboid also accepts an object of type Cuboid to calculate its volume.

One more thing that I would like to clarify here is that interfaces are present only in TypeScript, not JavaScript. As a result, compiling the above code will generate the following in JavaScript.

TypeScript also has a concept of intersection types where we can use the & operator to combine two interfaces. The resulting type will have members of both interfaces. Here is an example:

In the above code, we created a new type RoundedRectangle using two predefined types. This way, the RoundedRectangle type expects three properties.

We also could have created a new RoundRectangle interface in the above example, and our function would still have calculated the area properly. However, there are some differences which would affect your decision of going with intersection or extension of interfaces. For example, multiple interface declarations with the same name are acceptable and will be merged. On the other hand, type redeclarations will result in errors.

Final Thoughts

This tutorial introduced you to interfaces and how you can use them to make sure you are writing more robust code. You should now be able to create your own interfaces with optional and read-only properties.

You also learned how to use index signatures to add a variety of other properties to an object that are not included in the interface declaration. This tutorial was meant to get you started with interfaces in TypeScript, and you can read more on this topic in the official documentation.

In the next tutorial, you will learn about classes in TypeScript.

Did you find this post useful?
Want a weekly email summary?
Subscribe below and we’ll send you a weekly email summary of all new Code tutorials. Never miss out on learning about the next big thing.
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.