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.
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:
The code above doesn’t have any errors, because the resulting
IUser interface will contain all 3 properties —
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.
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:
This can be useful when we want to combine component’s own props with some HOC’s props that might use union operator.
It’s simply faster to type
type Props than
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
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.
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!
Shameless plug: if you want to learn how to use Redux without writing a ton of boilerplate, check my "State management with Rematch" course on Youtube.