Some time ago, I came to the conclusion that the TypeScript
type keyword should almost always be favoured over the
interface keyword. I arrived at this conclusion after considering the use cases and implications. Of course, there is a large overlap, but I personally believe that interfaces (in TypeScript) are a more restrictive and less useful construct than types.
When I first used TypeScript, I was surprised that two language features could be so similar as to appear almost indistinguishable.
- Both allow for declaring a contract or shape of a type1
- Both can be members of a union type
- Both can be extended to create derived types
- Both can be used for OO inheritance with classes and the
Types are more widely applicable
The type construct is more general purpose than interfaces. Consider the following code block.
Not only was a contract or shape of a user-defined, but a union type was also defined. A clear advantage is revealed; types can express multiple constructs and are more versatile than interfaces.
Union types are also explicitly part of the syntax, such as
string | number. That is, types are a more natural and everyday occurrence in TypeScript. For example, consider the following code block.
What has been achieved here compared to the first code block, other than extra characters and using two keywords and language features over one? Not much - other than confusion when revisiting the codebase and raising questions as to why both were used.
Using types is more consistent
The previous section demonstrates that types are less restrictive and surpass the use cases of interfaces. Continuing to use both keywords is simply inconsistent for no good reason. Making the choice between them is not difficult. The stricter but more versatile keyword or the looser and less useful keyword?
Interfaces can be merged (that’s rarely good)
Interfaces support declaration merging. This is definetly a “feature” to be cautious of. It has two good use cases - creating or extending the typings of third party libraries and declaring environment variables. It’s the relative complement of the Venn diagram (the tiny bit of orange that does not intersect the blue).
Outside of those two use cases, I do not see any value in the following code block. It’s simply confusing, especially if multiple interfaces with the same name are created throughout the code base.
Interfaces lean more towards OO thinking
Interfaces are, by their nature, strongly tied to classes. I would even suggest that interfaces were added to TypeScript specifically to support the class-based OO paradigm.
I can only think of two examples where I used classes or OO with TypeScript recently. This strongly contrasts with a typical project using UI libraries like React, where simple functions + data (often in the form of hooks) and declarative patterns and concepts are commonplace2.
This is a topic I’d like to expand on more in another article, so I’ll finish this section with an insightful talk from Douglas Crockford.
Types can still be used with classes
Again demonstrating the versatility of types, I created a customer processor in a typical OO style. There are two types available for implementation, and both are examples of higher-order functions (or in OO terminology: dependency injection). No interfaces here, yet still featuring composition.
Interface Hungarian Notation
Developers often bring conventions, patterns, and idioms from other languages they know. This can be good, as functional concepts like immutability, pure functions, and composition over inheritance work well in OO languages.
However, using naming conventions from one language in definitely not something to bring across. For example, C# interfaces starting with “I” are not idiomatic in TypeScript, but they are idiomatic C#. The point of a naming convention is to make code more readable and consistent.
I’ve explained my preference and why I believe type should be the default choice in your projects. It’s up to you which you use, but I hope you can see the benefits of types and why they are a better choice in most cases.
The inevitable angry comments can be directed to
The term “types” in TypeScript can refer to any object, be it a type alias or an interface. However, in the TypeScript ecosystem, the word “type” is typically usually used to specifically refer to the
typekeyword. This distinction is generally understood, even amongst pedants who are particular about precise language usage. ↩