TypeScript: types vs. interfaces #64
Description
TypeScript has two ways to describe the shape of an object: a type alias and an interface. I would like to collect some arguments for the discussion around types vs interfaces.
Historically, interfaces were more powerful which is why a lot of people went with interfaces. But nowadays the differences are not that big anymore. To summarize:
Type aliases don't create new names in error messages(not correct anymore)Type aliases cannot be implemented by classes(not correct anymore, only union types are not implementable which are impossible to do with interfaces anyway)- Two type aliases with the same name throw an error whereas two interfaces with the same name are merged into a single one (known as "Declaration merging"). This is an advantage of interfaces when dealing with third-party libraries, but can be a disadvantage if there is a name conflict in the current file (Example).
- Mapped types via
Key in AllowedKey
are impossible with interfaces (Example) - Interfaces can reference itself in its definition via
this
. Type aliases need to use the type alias name (which can be impossible sometimesTypeScript 3.7 supports recursive types) (Example) - Interfaces have the polymorphic
this
type. Some inheritance patterns cannot be expressed via types (Example) CodeLens In VSCode has better support for interfaces(not correct anymore, see below)- Intersection types can create types that are impossible to implement, whereas interfaces will show an error at the
extends
keyword (Example) - On the other hand, types can express intersection types that are impossible to express with interfaces (e.g. merging of types that overlap, like
string
and"some string"
) (Example) - Interfaces must always have a name. Types can be inlined without giving them a name, e.g. when instantiating the
React.FC<{}>
generic.
Personally I prefer type aliases because of the following reasons:
1. It's less to write (and less to read)
type A = AnotherType & ADifferentType & {
a: boolean;
};
// vs
interface A extends AnotherType, ADifferentType {
a: boolean;
}
Ok, that's not a big difference, but look at the next example:
const Component: React.SFC<Readonly<{
a: boolean;
b: string;
c: number;
}>> = ({a, b, c}) => {};
// vs
interface ComponentProps {
readonly a: boolean;
readonly b: string;
readonly c: number;
}
const Component: React.SFC<ComponentProps> = ({a, b, c}) => {};
2. It's a single way to handle types
One problem I see is that you can't really use interfaces in an ergonomic way when you want to use TS' utility types, like Readonly
:
// we have to create a mutable interface first
// before we can create a readonly type :(
interface A {}
type B = Readonly<A>;
type
on the other hand provides a single way to define and merge types as you like. For instance, union types are impossible with interfaces, but straightforward with type aliases.
In general I think it's impossible to have a big project without using type
, but it's possible to have a big project without interface
. We used type aliases in a big project and never really missed interfaces.
For me there are only three valid reasons for interfaces:
Nicer hints in VSCode
Maybe they will improve it for type
as well. I don't see a reason why this is only implemented for interfaces. It's possible to get a CodeLens for type references though (see below).
Third-party libs
It's often recommended to use interfaces for third-party libs because of declaration merging. This makes it possible to extend the type with own properties.
While this is true for libraries like Express that provide a single Request
or Response
object which is extended by middlewares, I don't think that it's true for third-party libraries that don't support this kind of API. Personally I even think that it's bad practice to have an API where objects can be extended by everyone. So why should I use interfaces if I don't want to support this kind of usage anyway.
OO programming style
If your project/team heavily uses an object-oriented programming style interfaces make much more sense.
What do you think?