FE / ts / js

Type is the ultimate. Type eclipses everything.

The day has come. You staring at the screen. In front of you an interface and a type. You’re wondering. They seem to do the same thing. Is it possible?! Are them really doing the same thing TypeScript?

What’s the difference? Why should I use one instead of the other? Did I use them wrong all along? All fair questions…

Once upon a time…

Back in the day, when OOP was cool, interfaces were meant to group methods so that classes could implement them.

And this was nice as it imposed a constraint on classes.
If the class did implement an interface you knew the interface methods were there.

interface is a group of related methods with empty bodies
— The Java™ Tutorials

At the same time in other languages, there were data structures that were meant for organising, managing and storing data.

And this was nice too because with data structures you focus just on data, no stress on business logic.

It may seem a subtle distinction but it’s quite important.

Having a clear separation data vs business logic means you have a clear understanding of the information you want to manage vs the algorithms that actually do the work for you.

Now, there is a particular data structure called object.
objects are a little bit funny as they can be data only — as such, they behave as pure data structures — or they can have business logic. In that case, you mess with methods like sendConfirmationEmail or createUserAccount kind of thing.

Note: the mere fact you have a method doesn’t mean you’re dealing with business logic though.

For example, there are data structure in JavaScript like Array which are data structure but at the same time have methods like push, pop, filter and map.

Welcome TypeScript

When TypeScript first appeared in 2012 it had to deal mainly with objects.

This is because JavaScript didn’t have classes back then.
In fact, JavaScript introduced classes only in 2015 with ES6.

But TypeScript, despite being a JavaScript superset, included several ES6 proposed features and shipped with classes.

And so, with support for objects and classes, TypeScript extended the scope of interface to model objects as well.

For unusual that it is from an OOP perspective, in this context, it made plenty of sense having interfaces to spec methods and properties.

Interfaces provide the ability to give names to object types and the ability to compose existing named object types into new ones.
— TypeScript Language Specification - Interface : 2013

No object, no interface

But what happens when you want to rename a number into something more meaningful, let’s say a PhoneNumber? In that case, you’re not dealing with an object, you can’t use an interface there.

And so, in 2015 with TypeScrpit 1.4, type aliases were introduced.

Because of type aliases, you can have renamed primitives which are types like type Coordinate = number or type PhoneNumber = number and union types which are types like type ButtonType = 'submit' | 'reset' | 'button'.

Unlike an interface declaration, which always introduces a named object type, a type alias declaration can introduce a name for any kind of type, including primitive types and union types.
— TypeScript Language Specification - Type Aliases : 2015
// CASE : INTERFACE AND TYPE 
// interfaces and types are interchangeable

// declare the shapes of objects
interface Point {
  x: number;
  y: number;
}

type Point = {
  x: number;
  y: number;
}

// function types
// note: this is possible because functions are
// objects in javascript
interface GreetFunction {
  (a: string) => void;
} 

type GreetFunction = (a: string) => void; 


// CASE : TYPE ONLY 
// you can't do this with interfaces

// renamed primitives 
type Coordinate = number;

// union types
type StringNumber = string | number;

// variadic tuple types 
// Introduced in Typescript 4 
type Strings = [string, string];
type Numbers = [number, number];
type StrStrNumNumBool = [...Strings, ...Numbers, boolean];

We’re the same but not quite

A part of rename primitives and union types, are interfaces and types the same?

Well, kind of.

If you read the docs Differences Between Type Aliases and Interfaces, you’ll find listed a cryptic difference named multiple merged declarations.

multiple merged declarations means that interfaces support overloading but types don’t. So, you can keep extending an interface but you can’t with type.

// Probably the other big unusual difference is multiple merged declarations
// declaration := when we use the interface or type keyword
// multiple    := more than one declaration
// merged      := the declarations get combined

// declaration 1
interface Point {
  x: number;
}

// declaration 2
interface Point {
  y: number;
}

// The two declarations get merged
// now Point is {x, y}


// with type it would give an error 

type Point = {
  x: number;
}

type Point = {
  y: number;
}

// Error Duplicate identifier 'Point'.(2300)

Which one should I use then?

Warning, opinions ahead.

The official docs recommend using interfaces when possible and types only if in need.

However, I wonder if we wouldn’t be better off having types for data structures and interfaces for business logic (methods and classes).

In this way, the use of interfaces would fit back to the original OOP meaning, leaving type as synonym for data.

This would hide the prototype side of JavaScript even more I guess and probably would push people even further towards classes but it would be for the best in the long run.

Summary up

We looked at the historical context of interface and data structure.

Then we saw the evolution of interface and type in TypeScript. Next, we listed the main interface-type differences which are renaming primitives, union types and multiple merged declarations.

After that, I speculate about the use of interface vs type, in particular interface for business logic and type for data structure.

With all this mental frame, you should have enough context to pick the best approach for your project.

If you want to dig further, I would recommend reading TypeScript Language Specification: 2013 Chapter 7 Interfaces, TypeScript Language Specification: 2015 Section 3.9 Type Aliases to see the evolution interface-type and lastly, Douglas Crockford Classical Inheritance in JavaScript if you’re into the prototype side of the force.

Comments