Extending TypeScript Types with Intersection

June 1st 2018 TypeScript

In JavaScript, it's really easy to just add another field to an existing object. Of course, there is a way to express that in TypeScript. Actually, there's even more than one way to do it.

Simply extending an existing interface is probably the most obvious one:

interface LocationWithDistance extends Location {
  distance: number;
  roadDistance: number;
}

However, this approach can become difficult to maintain. If you want to add the same fields to multiple different interfaces, it requires you to list the same fields for every single one of them. To avoid that, you can define the fields as a separate interface:

interface Distance {
  distance: number;
  roadDistance: number;
}

Now, instead of listing the fields everywhere, you can reference this interface instead:

interface LocationWithDistance extends Location, Distance { }

Still, for each interface you want to extend, you need to define a new matching extended interface. It would be nice to be able to simply apply this extension to any existing interface without having to define an interface for that first. One would think, it could be done with generics:

interface WithDistance<T> extends T {
  distance: number;
  roadDistance: number;
}

let instance: WithDistance<Location>;

Unfortunately, this doesn't work. The compiler complains with the following error:

An interface may only extend a class or another interface.

TypeScript has other tools at disposal, though. Instead of generics, an intersection type can be used:

let instance: Location & Distance;

With this construct, the variable is declared to contain the fields of both interfaces which is exactly what you want to achieve. By defining a type alias, there's no need to directly use the intersection when declaring variables:

type WithDistance<T> = T & Distance;

let instance: WithDistance<Location>;

Although the type is defined differently, the syntax for declaring the variable becomes identical to generics.

Copyright
Creative Commons License