Interface vs Type alias in TypeScript 2.7

People often ask me ( online, at work, in skatepark (nope 😂) ), what’s the difference between using type and interface for defining compile time types within TypeScript.

First thing I used to do, was pointing them to TypeScript handbook…

Unfortunately most of the time, they didn’t find the droids that they were looking for ( it’s hidden within Advanced Types section). Even if they found it, the information described there is obsolete ( described behaviour is for typescript@2.0.x ).

Good news everyone! You don’t have to look any further, This post is an up to date description/style-guide about when to use interface vs type alias.

What official documentation says:

“type aliases can act sort of like interfaces, however, there are some subtle differences.”

that’s correct !

What differences?

1. “One difference is, that interfaces create a new name that is used everywhere. Type aliases don’t create a new name — for instance, error messages won’t use the alias name.”

that’s incorrect ! (since TypeScript 2.1)

Let’s define compile time types for Point via interface and type alias and 2 implementation of getRectangleSquare function which will use both interface and type alias for parameter type annotation.

Point defined with interface and type literal
getRectangleArea functions using interface and type alias
Same error reference for both type alias and interface

So errors are same for both:

// TS Error: 
// Interface:
Argument of type '{ x: number; }' is not assignable to parameter of type 'PointInterface'. Property 'y' is missing in type '{ x: number; }'.
// Type alias:
Argument of type '{ x: number; }' is not assignable to parameter of type 'PointType'. Property 'y' is missing in type '{ x: number; }'.
2. “A second more important difference is that type aliases cannot be extended or implemented from”

Again, that’s incorrect!

We can extend an interface with type alias:

interface extended with type alias

Or use type alias for implementing a Class constraint

class implements type alias

Or use interface extended by an type for implementing a Class constraint

ThreeDimension extends PointType. PointType is an type alias

We can also combine both type alias and interface for implementing a Class constraint

class implements interface and type alias
3. “type aliases cannot extend/implement other types”

Again, that’s incorrect!

Well it’s partially correct but the formulation is miss leading 👀.

You can use interface or any other TypeScript valid type(which has shape of an Dictionary/JS Object, so non primitive types etc…) for type alias extension via intersection operator &

class implements type alias created with intersection

We can also leverage mapped types for various transforms of both interface and type alias.

Let’s make Shape and Perimeter optional via Partial mapped type:

class implements type alias created with intersection and mapped type. perimeter() and area() are optional so we don’t have to implement them within our class

Also weak type detection works correctly:

Weak type detection works as expected

Hybrid Types with both type alias and interface

You might occasionally want to define an object that that acts as both a function and an object, with additional properties.

What we are talking here about, is defining a type for a function ( callable object ) and static properties on that function.

This pattern might be also seen, when interacting with 3rd-party JavaScript, to fully describe the shape of the type.
Hybrid type definition and implementation

It works equally with type alias!

Hybrid type via type alias

There is a very suble difference though. You will get the particular shape type in IDE instead of refference to the Countertype.

difference between using type alias and interface for hybrid type

What is usually a good idea/practice, is to dissect our hybrid definition in two parts:

  • callable object (function) type alias
  • static properties object shape

and final Counter type:


So what’s the difference between type alias and interface again 🤖?

1. you cannot use implements on an class with type alias if you use union operator within your type definition

This will trigger compile errors:

1st difference — type alias union cannot be used for `implements`
Which makes complete sense! A class blueprint, cannot implement one or another shape structure, so nothing surprising on this front.

Where type alias union usage makes sense and also works, is for object definition via object literal. So following is valid and will produce compile error, because our object has to define one of perimeter() or area() methods, or both:

Union type alias — correct usage with object literal

2. you cannot use extends on an interface with type alias if you use union operator within your type definition

2nd difference — type alias union cannot be used for `extends` on interface

Again, similarly to class implementsusage, interface is a "static" blueprint — it cannot exists in one or another shape, so it cannot be extended by union type merge.

3. declaration merging doesn’t work with type alias

While declaration merging works with interfaces, it fails short with type aliases.

What I mean by declaration mergin?:

You can define same interface multiple times, and its definitions will merge into one:

declaration merging

This doesn’t work with type aliases, because type is an unique type entity ( for both global or module scope ):

3rd difference — type alias doesn’t support declaration merging

Declaration merging via interfaces is very important, when we are writing 3rd party ambient type definitions for libraries that are not authored with TypeScript, so consumer has option to extend them, if some definition are missing.

Same applies if our library is written in TypeScript and ambient type definitions are generated automatically.

This is the only use case, where you definitely should always use interface instead of type alias !

⚛️: What should I use for React Props and State ?

In general, use what you want ( type alias / interface ) just be consistent, but personally, I recommend to use type aliases:

  • it’s shorter to write type Props = {}
  • your syntax is consistent ( you are not mixin interfaces with type aliases for possible type intersections )
// BAD
interface Props extends OwnProps, InjectedProps, StoreProps {}
type OwnProps = {...}
type StoreProps = {...}
// GOOD
type Props = OwnProps & InjectedProps & StoreProps
type OwnProps = {...}
type StoreProps = {...}
  • your public component Props/State implementation cannot be monkey patched and for that reason, consumer of your component should never need to leverage interface declaration merging. For extension there are clearly defined patterns like HOC and so on.

Summary

In this article we learned what is the difference between interface and type alias in latest TypeScript.

With that covered, we came to an conclusion what method of defining compile time types should be used in a particular scenario.

Let’s recap:

  • type aliases can act sort of like interfaces, however, there are 3 important differences ( union types, declaration merging)
  • use whatever suites you and your team, just be consistent
  • always use interface for public API's definition when authoring a library or 3rd party ambient type definitions
  • consider using type for your React Component Props and State

As always, don’t hesitate to ping me if you have any questions here or on twitter (my handle @martin_hotell) and besides that, happy type checking folks and ‘till next time! Cheers!