TypeScript: clash of types

Photo by Chris Chow on Unsplash

TypeScript: clash of types

Another view on the type vs interface debate

While surfing the web, I stepped into this vlog from Web Dev Simplified. In that video, its creator, Kyle, revamps the clash of TypeScript types and interfaces, describing why he stands for the type side, despite what's written in official documentation:

For the most part, you can choose based on personal preference, and TypeScript will tell you if it needs something to be the other kind of declaration. If you would like a heuristic, use interface until you need to use features from type.

Well, turns out I don't agree with this sentence and, like Kyle, I stand on the type side of TypeScript. Let's see the differences between the two.

Types can be aliased

Types can be used to alias other types, so that they are easier to type. There is no way to do such a thing with interfaces.

type MyType = Readonly<Array<string | Cookie<string | number>>>
const my: MyType = ...

Types can be combined

If you have two types, you can create other types combining those:

type A = { a: string };
type B = { b: number };

type Union = A | B;
type Intersection = A & B;

const uA: Union = { a: "hi" };
const uB: Union = { b: 123 };
const uAB: Union = { a: "hi", b: 123 };

const i: Intersection = { a: "hi", b: 123 };

Instead, if I use interface I get this:

interface A { a: string }
interface B { b: number }

interface ABIntersection extends A, B { }

const ab: ABIntersection = { a: "hi", b: 123 }

There is no way to express union types with interfaces. Note that we NEED to use inheritance to effectively implement an intersection with interfaces. More on this later.

Interface declaration merging

What scores for interfaces is declaration merging. Once a type is defined, there is no way to change it. This is not true for interfaces. Let's rework our intersection implementation with interfaces.

interface C { s: string }
interface C { n: number }

const myIntesectionObj: C = { s: "hi", n: 123 }

This is powerful especially for libraries. A library can define an interface and allow its final users to customize and enrich it.

Interfaces can type only objects

This is the main argument that scores for the type side. Interfaces can define only object types. Is that a limitation of interfaces? I would hardly think so.

Interfaces are the way objects are typed not just in TypeScript, but in any OOP language like Java, or C#. They are tied to the OOP world by design. By nature. They DEFINE objects, conceptually, and were ported to TypeScript to support OOP at type level even before ES6 classes were a thing.

This is why we need extends to manipulate interfaces. The action of extending an interface, implies the concept of hierarchy that, let me say it once again, lives inside the OOP world.

Interfaces have nothing to do with giving a type to literal objects or type combinations. Hence it does not make any to sense to compare them to type. There will always be gaps that cannot be filled as much as similarities, but it will always be a comparison between apples vs pears. Such comparison does not make any sense as they serve two different purposes:

  • Types are used to type anything that a Javascript program can define. They are blankets over Javascript primitive types and objects to prevent runtime type errors and empower developers with compile time type detection and assertion.

  • Interfaces are used to define object types. This comes from the OOP world. There is no Javascript blanketing here.

Conclusion

Even if interfaces and types are very similar in functionality, having both of them limitations compared to each other, they cannot be considered interchangeable. The difference lies in the reason of their existence in the TypeScript world.

My advice to a new TypeScript developer would be to always use types and fallback to interfaces if and only if:

  • you are modeling a domain using OOP. This is where OOP shines.

  • you want to take advantage of interface declaration merging.

When you don't know if you should press t or i on your keyboard, you're better off pressing t. Types are by design your default choice, unless you are working in an OOP fashion.

To the next bite!

References