TypeScript Interface Tutorial With Examples

By Sruthy

By Sruthy

Sruthy, with her 10+ years of experience, is a dynamic professional who seamlessly blends her creative soul with technical prowess. With a Technical Degree in Graphics Design and Communications and a Bachelor’s Degree in Electronics and Communication, she brings a unique combination of artistic flair…

Learn about our editorial policies.
Updated March 9, 2024

In this tutorial, we will learn all about TypeScript Interfaces. We will also compare TypeScript Type vs Interface:

We will start off by examining why TypeScript Interfaces are important by looking at an object-type scenario.

We will proceed to look at the other TypeScript Interface types implementations and lastly look at the two TypeScript Interface naming styles. Implementing its types is simply the underlying structure that dictates the contract in our application.

It specifies the syntax to be followed by classes. The structure specified by the TypeScript Interface must be adhered to by all the classes derived from that Interface. The TypeScript Interface isn’t converted into JavaScript by the TypeScript compiler.

=> Read Through ALL TypeScript Tutorials

Instead, the TypeScript compiler uses the TypeScript Interface for type checking, this is also referred to as structural subtyping or duck typing. The keyword interface is used to define a TypeScript Interface.

An In-Depth Look at TypeScript Interface

TypeScript Interface

A TypeScript Interface can include method declarations using arrow functions or normal functions, it can also include properties and return types. The methods can have parameters or remain parameterless.

Creating Objects

To begin with, let’s create an object in TypeScript just in the way we do it in JavaScript. For instance, we can create a user whose name will be Monster and age 30.

Displayed below is an example code with the user being the name of our variable and our type being an Object.

const user = {
name: 'Monster',
age: 30
};

The image below shows how TypeScript’s compiler understands the object’s properties.

how TypeScript’s compiler understands the object’s properties

As this is an object type, that is the reason for which we have the brackets and inside the brackets, we have the name, string, age, and number. What this means is that TypeScript out of the box can read all our properties and understand the object.

Let’s create one more user and name our new user as user2. For this user, let’s say that we assign it only to the name Jack. Now here is the problem, normally in JavaScript, we can create whatever objects we want.

Given below is an example code showing another object with different properties

const user2 = {
name: "Jack"
};

For example, to elaborate on this situation clearly, let us say we are working with users as entities in our project, and we have defined that our entity user must always have two properties i.e. name and age. In this particular case, we forgot that our user2 also needs to have the property age.

A lot of people find themselves making this kind of mistake, but we can’t really find any help from JavaScript in this case because we simply didn’t specify that anywhere! JavaScript doesn’t provide that capability or rather functionality where we can specify all the properties of some entity.

Contrary to JavaScript, with TypeScript, we can do exactly that. That is, specify the properties needed for a particular entity. In our current situation, we can specify the types we want, such that we can have our user whose type is an object with name as string and age as the number as its properties.

Given below is an example code that shows how to define types

const user: {name: string; age: number} = {
name: 'Monster',
age: 30
};

Next, we can just copy the specifications from the user and apply it elsewhere e.g., we can specify the same to user2 by simply pasting the specifications.

The image below demonstrates how to populate the code with the created type specification

const user: {name: string; age: number} = {
name: 'Monster',
age: 30
};

const user2: {name: string; age: number} = {
name: "Jack"
};

We are getting an error in user2 that the property age is missing in type since the TypeScript compiler expects the object to have both the name and age properties.

Image showing the missing type error

the missing type error

We can add the missing property, but the big problem here is that we want to specify this type only once and reuse it everywhere, but not a matter of duplicating it every time because we can make a mistake inside types specification and this will involve changing every bit of code where we pasted our property specification.

This is one of the major reasons why we need TypeScript Interfaces. So, what is a TypeScript Interface?

TypeScript Interface

TypeScript Interface is a special entity that helps us create objects with certain properties. Let us proceed and create one. For instance, we can begin by typing the word Interface which is a reserved word in TypeScript to create a TypeScript Interface.

Thus, when we create a TypeScript Interface called User and inside the Interface block, we specify that we want our type to have the name and age properties that will be of type string and number respectively.

Given below is an example code showing how to declare a TypeScript Interface

Interface User {
name: string;
age: number;
}

What is paramount is the code style, where it is expected that the TypeScript Interface starts with a capital letter. This is why in our code the word user begins with an uppercase and inside the TypeScript Interface block, we are specifying all the properties with their types the same way we did previously with the user and user2 objects.

Example code showing how to specify properties inside TypeScript Interface block

Interface User {
name: string;
age: number;
}

const user: User = {
name: 'Monster',
age: 30
};

const user2: User = {
name: "Jack"
};

We can now reuse this TypeScript Interface everywhere, for example, in our case we have reused the TypeScript Interface as a type on both the user and user2. As is evident from the code snippet above, first of all, we are ending up writing much less code, and secondly, we are getting almost the same errors.

The image below shows an example of a property type error while deriving from a TypeScript Interface

a property type error

We are getting an error that property age is missing in type { name: string }, which means that the name of type string is our const user as shown on the image above, but the age property is also required in Interface user.

This means that we are not just getting some plain object like an object with name and age, but we’re getting type user and this is much easier to debug and understand the code because instead of just objects, array, and strings, you see some defined names and normally this is the names of your entities inside the project.

Basically, this is the most difficult part of learning TypeScript. It’s not just about TypeScript code style, but it’s about trying to plan your architecture in entities and how they will communicate with each other. Thus, you need to create enough amount of entities or TypeScript Interfaces to cover all your cases.

You will also need to plan how they will communicate with each other via REST or SOAP API’s, but you should also not create too many TypeScript Interfaces because in that case, it will be impossible to support your project. You might end up having hundreds of TypeScript Interfaces for every case which really does not help.

We still have our previous error i.e. age is missing. Yes, we can just proceed and type age and number as its type and we will have fixed the problem. Let us say that we want to make age non-mandatory by default, but again the old properties were set such that both the TypeScript Interface properties are mandatory.

Optional Properties

On many occasions we will find ourselves working on properties that were initially set up for us, thereby forcing us to implement all our objects derived from the interface with the same properties set.

Suppose we want some objects to have some and not all properties, then the best option would be to mark these properties as optional by just adding a question mark as a postfix to the property name. For example, suppose we don’t always need the age property or suppose it can be undefined, then we need to put a question mark.

Given below is an example code showing how to define optional properties in TypeScript Interface.

Interface User {
name: string;
age?: number;
}

It is important that you recall putting the question mark directly after the property name, not after the colon. This means that the question mark should come before the colon, and we will not get an error anymore because we specified that age should not be mandatory.

The image below shows optional properties implementation of TypeScript Interface properties

optional properties implementation

Best Autocomplete with TypeScript Interfaces

We are not getting the type as an object with all properties, but we get it as a TypeScript Interface and this is really helpful when it comes to debugging. Also, it’s worth mentioning that TypeScript Interfaces give us the best autocomplete and type checking in the world.

Thus, suppose we proceed to type console.log so that we can log the user, we will just need to hit dot and we will get an autocomplete of all possible properties of this.

Given below is an example code on how to use the dot to trigger TypeScript Intellisense support

console.log(user.)

Given below is an image displaying the rich TypeScript Intellisense support

rich TypeScript Intellisense support

Normally in the JavaScript world, you can’t really get that kind of ability. In our code, we only have the properties that are specified inside our TypeScript Interface i.e., name and age, and we directly see that age is not mandatory, so it can be undefined.

Also, even if we are typing something that is not correct, there will be an automatic type check from TypeScript and it will say that the property name does not exist on the type user. In addition, TypeScript will proceed and inquire from us what we mean.

Thus, TypeScript is wise enough to understand that maybe we made a typo and it really helps a lot to make applications faster.

Functions in TypeScript Interfaces

In many cases, we find ourselves in dire need to define functions inside our TypeScript Interfaces, and so it is completely possible. Let us proceed and create a new function inside our Interface User, for example, we have a function with the name to get the message. Thus, we need to define it by including a bracket after the function name.

Example code to declare the TypeScript Interface function

Interface User {
name: string;
age?: number;
getMessage(): string;
}

This is how we normally create functions inside our TypeScript Interface and string is the return value. We can now proceed and use our function. After declaring our function, we now have some errors everywhere that the property getMessage is missing in the type as shown in the image below.

The image below shows an example error caused by an unimplemented TypeScript Interface function

error caused by unimplemented TypeScript Interface function

Let us now proceed and remove this error, we will do this by including the getMessage function as a property in both of our objects or rather entities i.e. user and user2. Secondly, we need to specify that our function returns a string for example Hello and we can then concatenate Hello with the name property.

Given below is an example code on how to implement the declared TypeScript Interface function

const user: User = {
name: "Monster",
age: 30,
getMessage() {
return "Hello" + name;
},
};

const user2: User = {
name: "Jack",
getMessage() {
return "Hello" + name;
},
};

That’s it, and we can also provide some parameters inside the function’s bracket if we need to, but it’s not mandatory. We can now try to use the dot on our console log and we’re getting a nice autocomplete for the user-created function.

The image below shows TypeScript Intellisense recognition of the newly implemented function.

TypeScript Intellisense recognition

Below is an example of a complete basic TypeScript Interface implementation code.

Interface User {
name: string;
age?: number;
getMessage(): string;
}

const user: User = {
name: "Monster",
age: 30,
getMessage() {
return "Hello" + name;
},
};

const user2: User = {
name: "Jack",
getMessage() {
return "Hello" + name;
},
};

console.log(user.name);

Readonly Properties

In TypeScript Interface, properties can be marked as read-only. During runtime, this will not change any behavior. If a property is marked as read-only, then during type-checking, it can’t be written to. If you try to assign to it, you will get the error “Cannot assign to prop because it is a read-only property”.

Syntax for a read-only property definition

interface SomeType {
readonly prop: string;
}

Index Signatures

Sometimes we initially don’t know the names of our type’s properties and we only remember the shape of the value. In such cases, we can simply describe the types of possible values by using the index signature:

The syntax for an index signature definition

interface MyStringArray {
[index: number]: string;
}

const theArray: MyStringArray = getStringArray();
const secondItem = theArray[1];

In the above example, the interface MyStringArray has an index signature. According to the index signature rule, when MyStringArray is indexed with a number, then it is supposed to return a string. As a rule, an index signature should always be a number or a string.

Index signatures make sure that all properties match their return types, they are also very useful when it comes to describing the dictionary. This is possible since the string index regulates that obj.property should also be available as obj[“property”]. If the property’s type and the string index’s type don’t match, the type checker will give an error.

Inheritance in TypeScript

TypeScript Interfaces, like classes, can extend to each other. This makes it possible to copy members of one interface to another interface. This makes it possible to segment your interfaces into reusable modules:

Given below is an example code for TypeScript Inheritance

interface Car {
color: string;
}

interface Audi extends Car {
plateNumber: number;
}

let audi = {} as Audi;
audi.color = "blue";
audi.plateNumber = 10;

Example code on how to create a set of all the interfaces by extending multiple interfaces

interface Car {
color: string;
}

interface NumberPlate {
plateNumber: number;
}

interface Audi extends Car, NumberPlate {
seatCapacity: number;
}

let audi = {} as Audi;
audi.color = "blue";
audi.plateNumber = 1234;
audi.seatCapacity = 7;

Hybrid Types

Due to the fact that JavaScript is flexible and dynamic in nature, it’s common to come across objects that act as a combination of many types. A good example of such an object is where an object acts as both an object and a function, with additional properties.

Syntax for hybrid type definition

interface Timer {
(start: number): string;
interval: number;
reset(): void;
}

function getTimer(): Timer {
let timer = function (start: number) {} as Timer;
timer.interval = 122;
timer.reset = function () {};
return timer;
}

let c = getTimer();
c(12);
c.reset();
c.interval = 5.4;

You may be working with 3rd-party JavaScript and may need to consider using patterns similar to our example above so as to clearly describe a particular type’s shape.

Interfaces Extending Classes

When you have an interface type that extends to a class type, it does not inherit the class implementation but inherits the class members. In simple terms, it’s like declaring all the class members without implementing them.

Interestingly, interfaces inherit all the members of the class including protected and private members of the superclass.

This further implies that an interface that extends private and protected members of a base class can only be implemented by that class or a child class of it. This comes in handy when you have a large inheritance dependency and need to specify that your code functions with only child classes that meet specific properties.

Besides inheriting from the base class, the child classes don’t have to be related in any way.

Example code on how TypeScript Interfaces extends classes

class Dashboard {
private state: any;
}

interface SelectableDashboard extends Dashboard {
select(): void;
}

class Button extends Dashboard implements SelectableDashboard {
select() {}
}

class Knob extends Dashboard {
select() {}
}

Arrays in TypeScript

We can describe array types using interfaces.

Syntax for declaring and implementing TypeScript Interface Arrays

interface clientsArray { 
[index:number]:number 
}
var ages: clientsArray; 
ages = [10, 18, 25]; 
console.log("My age is: " + ages[1]);

In the example above, we have declared clientsArray that returns a number. The index type in the array will always be a number such that we can point to an element using its index position for actions like retrieval etc.

Given below is the output of the TypeScript Interface Array example code:

the TypeScript Interface Array example code

TypeScript Interfaces Classes

We create classes by using the class keyword followed by the name of the class which starts with a capital letter.

Syntax for declaring a TypeScript class

class User{}
Interface User {
name: string;
age?: number;
getMessage(): string;
}

In the code above, we have a name collision because we already have a class with the name User and we also have a TypeScript Interface with the same name i.e. User. For some reason, we need to specify that this is specifically a TypeScript Interface and not a class. There are two popular ways on how we can do that.

Naming of TypeScript Interfaces

We have two methods of naming TypeScript Interfaces;

  • Using the I prefix
  • Using the postfix Interface

We can write a capital I letter at the beginning of an Interface name i.e., a prefix such that it becomes IUser.

Example code on how to use the I prefix in TypeScript Interface naming

Interface IUser {
name: string;
age?: number;
getMessage(): string;
}

The second way is to represent the TypeScript Interface by using the postfix Interface to form the word UserInterface instead of the ambiguous name User or the capital I letter prefix as shown in the image below.

Example code on how to use the Interface postfix in TypeScript Interface naming

Interface UserInterface {
name: string;
age?: number;
getMessage(): string;
}

The postfix style is more readable. To this point, we have learned the reasons as to why we need TypeScript Interfaces, how we can define them and why they are so important for the architecture of a project.

Typescript Type Vs Interface

TypeScript InterfaceTypeScript Type
Primitives can’t be used by interfacesPrimitives can be used by types
type AlertType = string
type AlertSize = number
Unions are not supportedUnions are supported
type AppType = 'web' | 'mobile' | 'desktop'
Can declare a tuple but its not as clear compared to type
interface WebAppInfo {
value: [name: string, type: 'desktop’ | 'Android' | 'web']
}
Declares tuples clearly
type WebApp = [name: string, type: desktop' | 'Android' | 'web' | ']
Defining functions using interfaces is tricky
interface openApp {
(): void
}
Types defines functions easily and directly
type openApp = () => void
Supports declaration mergingDoesn’t support declaration merging
Intersection cannot combine multiple interfaces into a single interface using the & keywordIntersection can combine multiple types into a single type using the & keyword
Supports inheritanceDoesn’t support inheritance

Conclusion

TypeScript Interface enables us to define the properties that we want our entities to have in our project, including their respective types. TypeScript Interface enables us to write fewer lines of code. This is because we will not need to repeat the properties every now and then, but instead we will just type the TypeScript Interface name.

Since TypeScript Interface enables us to avoid repetition, it also enables us to reduce the length of code, and thus we achieve less code and it also makes our code easier to maintain. When we have less and shorter code then that implies that our code is easy to read. TypeScript Interface can be utilized to implement a type in the class after it defines it.

TypeScript Interface can be used to define a function type by ensuring a function signature. We use the optional property using a question mark before the property name colon.

This optional property indicates that objects belonging to the Interface may or may not have to define these properties. Similar to other programming languages, classes can implement TypeScript Interfaces. To learn more about TypeScript Interfaces click here.

PREV Tutorial | NEXT Tutorial

Was this helpful?

Thanks for your feedback!

Leave a Comment