How Does An Interface Work In TypeScript?
TypeScript, just like many modern object-oriented languages, allows a developer to create an interface. But what is it, and how to use it?
An interface is a contract structure that defines the syntax that a class or object must follow.
This article explains how interfaces work and shows different use cases with complete code examples.
Let's get to it 😎.
The definition
An interface is a structure that defines the contract that an object or class must follow.
Using an interface, a developer can define the methods and properties that an object or class must implement.
To define an interface in TypeScript, you must use the interface keyword.
TypeScript does not convert interfaces to JavaScript. The TypeScript compiler uses them for type checking. If you do not define the return interface of a function, the compiler can implicitly infer it.
Here is an example of an interface:
typescriptinterface IPerson {
name: string;
age: number;
}
const person: IPerson = {
age: 27,
name: 'Tim'
};
This interface defines a shape with two required properties. To be valid, the person constant must follow the exact structure (same properties with the same data types) of the IPerson interface. You must follow the interface structure to avoid getting a TypeScript error.
To type-check an interface, you can define a type guard.
A developer can also extend an interface.
Also, TypeScript provides various utility types to help developers work with interfaces.
Defining an optional property
TypeScript provides support for optional properties inside interfaces. A developer must place a question mark after the property's name to define one.
Here is an example:
typescriptinterface IPerson {
name: string;
age?: number;
}
const person: IPerson = {
name: 'Tim'
};
const person2: IPerson = {
name: 'Bob',
age: 28
};
In this example, the age property is optional, that why the first constant works without it.
Defining a read-only property
Also, a developer can define a read-only property inside an interface by using the readonly keyword.
Here is an example:
typescriptinterface IPerson {
readonly name: string;
}
const person: IPerson = {
name: 'Tim'
};
// This doesn't work because the property is read-only.
person.name = 'Bob';
Note: You cannot modify a read-only property.
Indexable Types
Using an interface, you can also define an indexable signature.
Indexable signatures are helpful when you know the data types of the keys and values of an object, but its structure is unknown.
When defining the data type of the key of an indexable signature, you can only use a string, a number, a symbol, a template string pattern, or a union of those.
Here is an example:
typescriptinterface IndexSignature {
[key: string]: string;
}
const index: IndexSignature = {
prop1: 'value1',
prop2: 'value2',
};
In this example, we can add any string key/value pair to the index constant.
Note: If you want mapped properties, you must use a type instead of an interface.
Note: You can also use the Record utility type to achieve a similar result.
Read more: 4 ways to create a map in TypeScript.
Generic interfaces
TypeScript interfaces also allow the use of generic parameters.
A generic parameter is helpful when you do not know the data type of a property. In short, generics help create reusable components AND provide generic type-checking.
Here is an example:
typescriptinterface IPerson<T> {
name: string;
data: T;
}
const p1: IPerson<string> = {
data: 'blablabla',
name: 'Tim'
}
const p2: IPerson<number> = {
data: 20,
name: 'Tim'
}
In this example, we create two constants that implement the IPerson interface.
For the first constant, p1, we pass a string as the generic argument. That's why data can only be a string.
For the second constant,p2, we pass a number as the generic argument. That's why data can only be a number.
How to extend an interface?
If you want to add a property or method to an interface, you can extend the interface using the extends keyword.
Here is an example:
typescriptinterface IAnimal {
name: string;
}
interface IDog extends IAnimal {
bark: () => void;
breed: string;
}
In this example, we extend the IDog interface with all the properties from the IAnimal interface.
Note: You can extend multiple interfaces by passing them as a comma-separated list.
Read more: How to extend an interface?
How to implement an interface?
A TypeScript class can implement one or multiple interfaces using the implements keyword.
Here is an example:
typescriptinterface IAnimal {
name: string;
eat: () => void;
}
class Fish implements IAnimal {
public constructor(
public name: string
) { }
public eat () {
console.log('The fish is eating.');
}
}
const fish = new Fish('Fishy');
// "The fish is eating."
fish.eat();
In this example, the Fish class implements the IAnimal interface.
If one or more properties/methods from the interface are missing inside the class that implements it, the TypeScript compiler will output an error.
Read more: How to make a class implement an interface?
Difference between an interface VS a type
In TypeScript, a developer can use an interface or a type to describe named types. That's why many developers confuse them.
Here are the most significant differences between them:
- An interface cannot declare a primitive.
- An interface cannot contain mapped properties.
- A type cannot participate in declaration merging.
To learn more, read this article: Differences between a type VS an interface.
Difference between an interface VS a class
An interface and a class are two completely different concepts.
In TypeScript, a class is an object that exists at runtime and encapsulates related properties and methods.
On the other hand, an interface is a structure that doesn't exist at runtime, only used for type-checking a class or object.
Final thoughts
As you can see, defining an interface is easy in TypeScript.
Regarding its naming convention, some developers prefix all interface names with the capital letter I, and some don't. TypeScript's original developers advise not to prefix since the concept of a Typescript interface is much broader than in languages such as C#.
As for me, I prefer to prefix, to differentiate between types and interfaces.
Here are some other TypeScript tutorials for you to enjoy: