Write TypeScript like a pro. Typescript Icon

Follow the ultimate TypeScript roadmap.

TypeScript Interfaces vs Types

Many developers are confused when choosing between a TypeScript interface or a type. This is probably because they’re very similar with minor differences.

Let’s explore some sensible practices and defaults, whilst uncovering some hidden gems in the TypeScript language and answer the question “Should I use an interface or a type?”

Once you’re finished, check out my other article on TypeScript Interfaces vs Classes!

Here’s my rule: For use cases such as creating new types through things like primitives, union types, and tuple types, I prefer to use the type keyword. For anything else (objects/arrays), it’s an interface.

An interface is extremely helpful when dealing with data structures as they’re a very visual representation (albeit so is type, this is typically my preference). It’s completely okay to choose a type for your objects and arrays too.

Nevertheless, let’s uncover some more about Types vs Interfaces in TypeScript so you can make a more informed decision.

Objects: Type vs Interface

Typically we would define a data structure to model a type against our intended variables and function arguments. Most of the time a data structure is an array or an object that might contain a few methods.

Lets create an object as an interface (recommended approach):

interface Milkshake {
  name: string;
  price: number;
  getIngredients(): string[];
}

Here, I prefer using an interface because it’s clear it’s an interface and not a variable with data assigned - a nice readability win. We’re using name and price to allow setting records and getIngredients as our method call.

Angular Directives In-Depth eBook Cover

Free eBook

Directives, simple right? Wrong! On the outside they look simple, but even skilled Angular devs haven’t grasped every concept in this eBook.

  • Green Tick Icon Observables and Async Pipe
  • Green Tick Icon Identity Checking and Performance
  • Green Tick Icon Web Components <ng-template> syntax
  • Green Tick Icon <ng-container> and Observable Composition
  • Green Tick Icon Advanced Rendering Patterns
  • Green Tick Icon Setters and Getters for Styles and Class Bindings

When we compare that to a type - it could easily be confused as an actual object due to the assignment =:

type Milkshake = {
  name: string;
  price: number;
  getIngredients(): string[];
};

This is just my small preference for choosing an interface over using a type here - but you’re free to use the type keyword if you wish.

Intersecting: Type vs Interface

Intersecting simply means to combine one or more types! This pattern can be accomplished using both an interface or type.

Let’s assume the following interfaces, I’ll show you how we can intersect an interface and type:

interface MilkshakeProps {
  name: string;
  price: number;
}

interface MilkshakeMethods {
  getIngredients(): string[];
}

With an interface, we would intersect by using the extends keyword. I’m using multiple interfaces to show you how to extend (inherit) into a final Milkshake interface containing all properties and methods defined elsewhere:

// { name: string, price: number, getIngredients(): string[] }
interface Milkshake extends MilkshakeProps, MilkshakeMethods {}

We can now use Milkshake anywhere we like and can benefit from having name, price and getIngredients in just one interface reference.

Here’s how we’d do the same with a type and intersecting the types via &:

// { name: string, price: number, getIngredients(): string[] }
type Milkshake = MilkshakeProps & MilkshakeMethods;

I’d recommend using a type instead of an interface when you want to intersect types. Using extends feels a bit more verbose and not as clear to read and I feel this is what the type keyword was made for.

It’s also super easy to just combine more types with type. I find the code is clearer than using extends with intefaces.

Interface are also limited - the type alias can be used for more complex types such as tuples, primitives, unions and other more:

// { name: string, price: number, getIngredients(): string[] }
type Milkshake = MilkshakeProps & { getIngredients(): string[] };

Primitives: Type vs Interface

Let’s talk primitive types. Yes, strings, numbers etc. You can only assign a primitive type to a type alias:

type MilkshakeName = string;

interface Milkshake {
  name: MilkshakeName;
  price: number;
}

If you need to use a primitive, use a type. Interfaces are a no-go here for just a single value as the syntax doesn’t support it.

Angular Directives In-Depth eBook Cover

Free eBook

Directives, simple right? Wrong! On the outside they look simple, but even skilled Angular devs haven’t grasped every concept in this eBook.

  • Green Tick Icon Observables and Async Pipe
  • Green Tick Icon Identity Checking and Performance
  • Green Tick Icon Web Components <ng-template> syntax
  • Green Tick Icon <ng-container> and Observable Composition
  • Green Tick Icon Advanced Rendering Patterns
  • Green Tick Icon Setters and Getters for Styles and Class Bindings

For most cases though, it would easier to just use the primitive value directly:

interface Milkshake {
  name: string;
  price: number;
}

Don’t overcomplicate your code!

Classes: Type vs Interface

Whether you’ve chosen a type or interface the way we use it with a class is the same:

type Size = {
  size: string
};

interface Milkshake {
  name: string;
  price: number;
  getIngredients(): string[];
}

class Order implements Size, Milkshake {
  // Size
  size = 'large';

  // Milkshake
  name = 'Vanilla';
  price = 399;
  getIngredients() {
    return ['vanilla', 'ice'];
  }
}

Classes do not support implementing/extending union types, because they are considered to be static blueprints. This means you need to be super explicit about each type you implement, as it cannot be dynamic or change right now due to TypeScript limitations.

Learn more about TypeScript Interfaces vs Classes!

Functions: Type vs Interface

Typically I would create a function using the type alias as most of the time we would want to type an anonymous function:

type IngredientsFn = () => string[];

const getIngredients: IngredientsFn = () => ['vanilla', 'ice'];

However, if using an interface for this would be more your style, here’s how to do that:

interface IngredientsFn {
  () => string[];
}

It just feels a bit wonky compared to using type, but again both are completely valid TypeScript and there’s no difference when the code is compiled.

When not to use a Type

Now we’ve explored the various comparisons and recommended approaches, it’s time to talk about Declaration Merging, a feature in TypeScript that applies to just interface and would be a reason to choose an interface over a type.

To merge types using type, we would have to do something like this and create a new final type:

type MilkshakeProps = {
  name: string;
  price: number;
};

type MilkshakeMethods = {
  getIngredients(): string[];
};

type Milkshake = MilkshakeProps & MilkshakeMethods;

Our type Milkshake is now a type alias of MilkshakeProps and MilkshakeMethods. Note, that to achieve this behaviour we have had to create Milkshake, giving us 3 individual type aliases.

With interfaces, there’s in fact a smarter approach (some might say a bit too clever) called declaration merging.

We can achieve a declaration merge by simply declaring the same interface twice in the same scope (this could be either via importing the interface from another module, or declaring it locally next to another interface):

// declared once...
interface Milkshake {
  name: string;
  price: number;
}

// declared again the same, it works!
interface Milkshake {
  getIngredients(): string[];
}

// { name: string, price: number, getIngredients(): string[] }
const milkshake: Milkshake = {
  name: 'Banana',
  price: 399,
  getIngredients() {...}
};

Milkshake now contains name, price and getIngredients! However, is declaration merging a good thing? I’m honestly not a fan and I feel it could lead to more harm than good. I’d compose your types through type rather than use declaration merging - but at least you know the main difference(s) now.

Conclusion

So there you have it! The main differences between Types and Interfaces in TypeScript.

To recap, with some personal preferences too, I’d stick with an interface for objects and use the type alias keyword to compose new types on the fly. These new types could even be from interfaces or other types such as tuples, unions and intersection types. There’s a lot of possibilities, but by understanding approaches we can begin to pick the right tool.

For the most part, interfaces and types are pretty much the same besides a few differences covered here. I hope you enjoyed reading!

If you are serious about your TypeScript skills, your next step is to take a look at my TypeScript courses, they will teach you the full language basics in detail as well as many advanced use cases you’ll need in daily TypeScript development!

Happy coding!

Learn TypeScript the right way.

The most complete guide to learning TypeScript ever built.
Trusted by 82,951 students.

Todd Motto

with Todd Motto

Google Developer Expert icon Google Developer Expert

Related blogs 🚀

Free eBooks:

Angular Directives In-Depth eBook Cover

JavaScript Array Methods eBook Cover

NestJS Build a RESTful CRUD API eBook Cover