TypeScript Type vs. Interface: Which One Should You Use?

Robin
Updated on March 13, 2023

TypeScript provides two primary ways to define custom types: using type declarations and using interface declarations. While these two constructs might seem similar at first glance, they have some key differences in their behavior and capabilities.

In this post, we'll take a closer look at the differences between type and interface declarations in TypeScript, and explore the use cases where you might want to use one over the other.

We'll start by understanding the basics of type and interface declarations and then dive deeper into their differences.

Understanding the Basics: Type and Interface Declarations

The type keyword allows you to define a custom type alias for an existing type or create a new type altogether. The type declarations are very flexible and can be used to define a wide range of types, including primitive types, object types, union types, and intersection types.

          type Person = {
  name: string;
  age: number;
  email?: string;
};
        

In this example, I have defined a new type called Person that has three properties: name, age, and email. I am making the email property optional by adding the ? symbol after its name.

On the other hand, interface declarations are primarily used to describe the shape of an object. An interface can define properties and their types, methods and the types of their arguments.

Let's declare an interface for a custom Person object:

          interface Person {
  name: string;
  age: number;
  email?: string;
}
        

As you can see, the syntax for an interface declaration is similar to that of a type declaration. However, you can only use interfaces to define object types, while types can define a wider range of types.

Let's compare type and interface declarations in more detail and discuss when to use each one.

Differences Between Types and Interfaces in TypeScript

The main difference between type and interface declarations is that interface describes the shape of an object, whereas type declarations are more flexible and can define complex types, including primitive types, arrays, and tuples.

Interfaces are extendable and allow adding new properties through re-opening or declaration merging. On the other hand, types use intersections to extend indirectly.

This is a list of key differences between type and interface declarations:

  • type declarations allow you to create new types by combining existing ones, while interface declarations describe the shape of an object.
  • TypeScript will merge 2 interfaces with the same names but 2 type declarations can't have the same names.
  • type declarations can define primitive types, literal types, and tuple types, while interface declarations cannot.
  • interface declarations can extend other interfaces to inherit their properties, whereas type declarations cannot be extended.
  • type declarations can create aliases for existing types, including built-in types like string and number, whereas interface declarations cannot create aliases.
  • A class can implement an interface but it can't implement type declarations.

I will discuss how you should choose between type and interface for your project later in this post. I will also give you some tips so that you can make your choice easily.

Also Read: Mastering Type Casting in TypeScript: A Complete Guide

Merging of Declaration

In TypeScript, you can merge interface declarations with the same names to create a single interface that will contain all the properties and methods of both declarations. But it is not possible to type declarations.

          interface User {
  id: number;
  name: string;
}

interface User {
  email: string;
}

const user: User = {
  id: 1,
  name: 'John',
  email: 'john@example.com'
}
        

In this example, I have 2 User interfaces. As they are using the same name, TypeScript will combine both of them automatically. When I use this User interface later, it will contain all the properties of both interfaces.

Now, you can create an object of the type User that has all three properties: id, name, and email.

But if you try to do the same thing with the type keyword, it will give you an error. Because in TypeScript, you are not allowed to give multiple types the same name.

Extends and Implements

In TypeScript, you can extend an existing interface to create a new interface that includes all of the properties and methods of the original interface, as well as any additional properties or methods that you define.

          interface Animal {
  name: string;
  makeSound(): void;
}

interface Dog extends Animal {
  breed: string;
}

const myDog: Dog = {
  name: "Fido",
  breed: "Golden Retriever",
  makeSound() {
    console.log("Woof!");
  }
};
        

In this example, the Dog interface extends the Animal interface by adding a breed property. The myDog object will contain properties and methods from both the Animal and Dog interfaces.

A class can implement an interface. In this way, you can make sure that this class includes all of the properties and methods of the interface. You need to use the implements keyword for that.

          interface Shape {
  area(): number;
}

class Square implements Shape {
  constructor(public name: string, public width: number) {}

  area() {
    return this.width * this.width;
  }
}

const mySquare = new Square("My Square", 5);
console.log(mySquare.area()); // 25
        

Here, the Square class is implementing the Shape interface. If I don't define the area() method in this class, TypeScript will show an error. It will ensure whenever I create an object using this Square class, that object will have the area() method.

But you can't use these extending and implementing features with type declarations. It doesn't support these functionalities.

Intersection and Union

Intersection types help you to combine multiple types into a single type that includes all of the properties and methods from each type. You use the ampersand symbol & to combine the types.

          interface Shape {
    color: string
}

interface Dimensions {
    width: number
    height: number
}

type ShapeWithDimensions = Shape & Dimensions

const square: ShapeWithDimensions = {
    color: 'red',
    width: 10,
    height: 10,
}
        

I have 2 interfaces Shape and Dimensions. I have defined a ShapeWithDimensions type by combining both of those interfaces. You can also join 2 type declarations to create a new type.

          type Shape = {
    color: string
}

type Dimensions = {
    width: number
    height: number
}

type ShapeWithDimensions = Shape & Dimensions

const square: ShapeWithDimensions = {
    color: 'red',
    width: 10,
    height: 10,
}
        

Here I am using types instead of interfaces. Both of them will work in the same way. Therefore it's totally up to you which one you want to use.

It is important to note that in both cases, we are creating a type declaration by using the intersection. You can't combine multiple types or multiple interfaces to create another interface.

Union types also create a new type but in a different way. It combines different types into a single type, where a value of that type can be one of the types. You use the pipe symbol | to separate the types you want to combine.

          interface Square {
  kind: 'square';
  size: number;
}

interface Circle {
  kind: 'circle';
  radius: number;
}

interface Triangle {
  kind: 'triangle';
  sideLength: number;
}

type Shape = Square | Circle | Triangle; // union type

        

I have three interfaces Square, Circle, and Triangle. I have then defined a new type Shape that is a union of these three interfaces. The value of this Shape type will be either a Square, Circle, or Triangle.

The type you get from a union always will be a type, not an interface. Both intersection and union will always return a type declaration.

Other Types

Unlike interfaces, types have more flexibility. You can declare a type using any primitive type like string, number, boolean, etc. But you can only use interfaces to define the structure of an object.

          // primitive
type Age = number

// object
type Person = {
    name: string
    age: Age
}

// tuple
type Info = [string, boolean]
        

Here I am creating different types to store different structures. It is also possible to define a tuple type. But you don't have such flexibility with interfaces.

But you can use all those structures inside an interface as its property type.

          interface Person {
    name: string
    age: number
    address: {
        zip: number
        city: string
        country: string
    }
    info: [string, boolean]
}
        

Here inside this Person interface, I am using primitive types, objects, and tuples.

Tips for Choosing Between Type and Interface Declarations

The choice between type and interface declarations largely depends on your specific use case and preference. Both have their strengths and weaknesses, so it's important to understand the differences and choose the one that best fits your needs.

These are some situations where you can use interfaces:

  • Use interfaces when you want to extend them or want to implement them in a class.
  • Use interfaces when you want to define the shape of an object or a class. This is because interfaces are more suited for defining object types.
  • Use interfaces when you want to use declaration merging. With this feature, you can modify an existing interface by creating another interface with the same name.

These are some situations where you can use types:

  • Use types when you need to use union or intersection types.
  • Use types if you need more flexibility over your type aliases and want to create types.
  • Use types when you want to create an alias for primitive types, function types, or tuples.

Also Read: TypeScript Function Overloading: Learn Best Ways to Use It

Conclusion

Both type and interface declarations are essential features in TypeScript for defining types. When it comes to choosing between type and interface declarations, there is no right or wrong answer.

If you want to define the shape of an object or a class, interfaces are more suitable. On the other hand, if you need to create a complex alias for a type or define a union or an intersection, types are the way to go.

The choice largely depends on your specific needs, preferences, and use cases. That's why It is important to understand these differences between type and interface in TypeScript and choose the appropriate declaration for your specific use case.

Related Posts