Type aliases vs. interfaces in TypeScript-based React apps

Published on April 27, 2019

cover picture
Photo by Artem Sapegin

Type aliases and interfaces are TypeScript language features that often confuse people who try TypeScript for the first time. What’s the difference between them? When should we use one over the other?

Type aliases and interfaces used to be quite different in their capabilities. However, it’s no longer true in the latest versions of TypeScript. Over time they have grown together to the point when they are almost identical. They still have some subtle differences — interfaces are more “extendable” due to the support of declaration merging, and types are more “composable” due to support of union types. We’ll talk about these differences in more details a bit later.

Owing to the nature of differences between type aliases and interfaces, the decision to use one over another usually depends on your preferred programming style. If you write object-oriented code — use interfaces, if you write functional code — use type aliases.

Now let’s talk about React.

React is more functional by nature. Functional components are generally preferred over class-based components. Hot and shiny React Hooks are just functions used inside functional components. HOCs, Redux, pure functions, and a handful of other concepts widely used in React come from a functional world. So for these reasons,

In React applications, just use type aliases.

Now let’s see why.


1. Power of interfaces isn’t useful in React applications

One of the main things that differentiate interfaces from type aliases is the ability to merge declarations. With interfaces, we can re-open previously declared interfaces and add additional properties to it:

interface IUser {
firstName: string
lastName: string
}
interface IUser {
age: number
}
const user: IUser = {
firstName: 'Jon',
lastName: 'Doe',
age: 25,
}

The code above doesn’t have any errors, because the resulting IUser interface will contain all 3 properties — firstName, lastName, and age.

This is a very powerful feature that allows us to write type declarations for 3rd party libraries and gives us an option to extend them. However, In regular React application, this feature doesn’t bring any value. On the contrary, it can introduce unnecessary complexity and add bugs if somebody will try to monkey-patch props or state interfaces.

2. Type aliases are more composable

One thing that type aliases can do that interfaces can’t is create an intersection with a type alias that uses union operator within its definition:

type CreditCard = {
number: string
expMonth: number
expYear: number
}
// Type with union operator
type Chargeable = { token: string } | { cvc: number }
type ChargeableCreditCard = CreditCard & Chargeable
/* ---------------------------------------
Interpreted type type will look like this:
type ChargeableCreditCard = (CreditCard & {
token: string;
}) | (CreditCard & {
cvc: number;
})
-----------------------------------------*/

This can be useful when we want to combine component’s own props with some HOC’s props that might use union operator.

3. Type aliases require less typing (as in typing on the keyboard)

It’s simply faster to type type Props than interface IProps.

4. Consistency

First of all, we don’t want to mix interfaces and types. It’s a common convention to prefix interface names with I, so we’ll have a mix of IProps interfaces and Props type aliases in our code. Not only will it create unnecessary clutter, but also increase required mental effort every time we need to think: “Do we have IProps interface or Props type aliases here?”

Second, we won’t be able to just use interfaces either. As we mentioned earlier, type composition is a very useful feature for React applications, and because interfaces cannot be combined with union-based types, at some point we may need to change our interfaces to type aliases. That means that we’ll also have to rename IProps to Props to avoid further confusion. And if the type is exported, we have to rename all the occurrences in the code as well. This extra work can be avoided by simply using type aliases everywhere.

Conclusion

Hopefully, this article helped you to see the difference between interfaces and type aliases in TypeScript and also convinced you that type aliases are preferable for React applications.

If you disagree with any of the points or feel that something is missing, please let me know in the comments. In the meantime, go ahead and disable interface-over-type-literal setting in TS Lint and start using type aliases in your React applications!