Interfaces in typescript are a very different thing from interfaces in C# and Java. Same name, but very different.
And if you avoid using them in a way that would be normal in C#, TypeScript interfaces can be valuable.
When Not to Use Interfaces
When I first jumped into TypeScript I tried to use it as I use C#. And that meant I had numerous interfaces that added additional functionality to sub-classes. For example, here are some objects for our internal Document class:
- Element – all objects in the document inherit from this.
- Paragraph – Inherits from Element and is a paragraph in the document.
- RunText – inherits from Element and is a run of text.
Now Paragraph has a body of objects (RunText, images, etc.) but RunText does not have a body. So in C# we have the IBodyElement interface. Any Element derived class that has a body implements this interface.
This (the interface) is a horrible way to do this in TypeScript. Totally wrong.
Second, the interface in C# enforces only making the body related calls if you cast to that interface. You then have that set of calls available with the casted var. Unfortunately TypeScript will not let you cast from Element to IElementBody, only from Paragraph to IElementBody. You can cast “myElement” but that takes away any compile time checking of the cast.
Create classes in TypeScript – that is powerful. But don’t use interfaces to add functionality to classes. I’ve yet to see a situation where that makes sense.
Where TypeScript Interfaces Rock
But there are two places where interfaces are very valuable.
First is when you receive JSON data. That data can be defined as an interface. The JSON data has no prototype (i.e., no methods) and therefore is an interface. Defining it as a class would be inaccurate, unless the class had no methods. And that is an interface.
Second is when you receive data from a web worker postMessage(). The received data is all of the data posted, with pointers between parts valid. But the prototype has been stripped. So again it is an interface.
The way we handle both of these cases is for every class, we have an interface it implements (IElement in the case of Element). And the copy constructor of each class takes as a parameter not the class, but the interface). This does a great job of forcing a match between the interface and class. And it defines the prototype-less objects as they truly are – the data but no methods.
Know of any other cases where TypeScript interfaces are useful? Share your experience in The Windward Studio Blog.
Author: David Thielen
Dave, Windward’s founder and CEO, is passionate about building superb software teams from scratch and dramatically improving the productivity of existing software teams. He’s really proud that he once created a game so compelling (Enemy Nations) that a now-professional World of Warcraft player lost his job for playing it incessantly on company time.
Other posts by David Thielen