In TypeScript, both an interface and a type alias can be used to describe a new named type. Since in most cases, almost all features are available in both, developers are sometimes using them interchangeably. This brings confusion and begs the question of type vs interface, which to use, and in which case.
The most important differences between a type vs interface are:
- An interface can participate in declaration merging, but a type cannot.
- An interface cannot declare a primitive (number, string, tuple, etc…), but a type can.
- Only a type can use mapped properties.
In this article, I will go over, in detail, all the differences between a type vs an interface and will explain which to use and when.
Let’s get to it 😎.
1. Primitive Types
A primitive type can only be declared with a type.
If you try to declare one with an interface, it will not work.
type AString = string;
type BNumber = number;
type CBoolean = boolean;
type DArray = string[];
type EUnknown = unknown;
type FAny = any;
type GNull = null;
type HUndefined = undefined;
2. Declaration Merging
Declaration merging is only possible with an interface.
In TypeScript, declaration merging happens when the compiler merges two or more interfaces of the same name into one. On the other hand, if you try to declare two types of the same name, the compiler will throw an error.
Here is an example of declaration merging in TypeScript.
interface IArticle {
content: string;
}
interface IArticle {
category: string;
}
const article: IArticle = {
category: 'news',
content: 'new content'
};
In this example, we first declare the IArticle interface with one property. Then, we declare it another time, with a different property. Finally, the compiler merges both interfaces into one, since they share the same name.
3. Inheritance and Extends
Both a type and an interface can be extended, but with a slightly different syntax.
Here is an example of how an interface can extend another interface.
interface IArticleContent {
content: string;
}
interface IArticle extends IArticleContent {
category: string;
}
Here is an example of how a type can extend another type.
type ArticleContent = {
content: string;
}
type Article = ArticleContent & {
category: string;
}
Note: With TypeScript, you can extend a type with an interface, and vice-versa, when using the extend feature.
4. Implements
Both a type and an interface can be implemented by a class.
Here is an example of how a class can implement an interface.
interface IArticle {
category: string;
content: string;
}
class Article implements IArticle {
public category = '';
public content = '';
}
Here is an example of how a class can implement a type.
type MyArticle = {
category: string;
content: string;
}
class Article implements MyArticle {
public category = '';
public content = '';
}
Note: You cannot implement/extend a union-type type alias.
5. Tuples
A tuple can only be declared with a type.
If you try to declare one with an interface, it will not work.
Here is an example of a tuple declaration in TypeScript.
type MyTuple = [ string, number ];
6. Intersection
Intersection types allow the developer to merge two or more type declarations into one.
You can use the intersection & operator to create a new type, both with types and with interfaces. However, the combined declaration must always be a type.
Here is an example of how to combine two types with an intersection.
type MyArticle = {
content: string;
}
type ArticleContent = {
category: string;
}
type Article = MyArticle & ArticleContent;
const article: Article = {
category: 'news',
content: 'new content'
};
Here is an example of how to combine two interfaces with an intersection.
interface IArticle {
content: string;
}
interface IArticleContent {
category: string;
}
type Article = IArticle & IArticleContent;
const article: Article = {
category: 'news',
content: 'new content'
};
7. Union
A union type allows the developer to create a new type that can be either type A or type B.
You can use the union | operator to create a new union type, both with types and with interfaces. However, the combined declaration must always be a type.
Here is an example of a union of two types.
type Cat = {
name: string;
};
type Dog = {
name: string;
};
type Animal = Cat | Dog;
Here is an example of a union of two interfaces.
interface ICat {
name: string;
}
interface IDog {
name: string;
}
type Animal = ICat | IDog;
8. Mapped Properties
Currently, you can only use mapped properties with a type.
Here is an example of a generic mapped type.
type MyConfig<T> = {
[Property in keyof T]: string;
};
f you try to do the same with an interface, the compiler will give you an error (A computed property name must be of type ‘string’, ‘number’, ‘symbol’, or ‘any’).
9. Performance
In earlier versions of TypeScript, there was a performance benefit of using an interface over a type, especially for intersections. However, since TypeScript version 4.2 was released, this performance benefit is no longer there, so you can choose to use either or.
Final thoughts
Although, in most cases, even if you can use either a type or an interface, you shouldn’t use them interchangeably… You should however create a rule and follow it in your TypeScript repository. It will help you and other developers understand easier and faster your code.
In my case, I always use an interface, unless I need to use a specific feature that only a type has (tuple or another primitive, mapped properties, etc…).
When do you use the interface vs the type? Write your answer down below (I always respond).
I hope you’ve enjoyed this article, please share it with your fellow TypeScript developers.