TypeScript: Interfaces vs Types - Understanding the Difference
Tags: webdev, typescript, frontend, programming
Published: 2024-05-28 11:05:36

---
canonical_url: https://antondevtips.com/blog/typescript-interfaces-vs-types-understanding-the-difference
---

This blog post makes a deep dive into TypeScript's object type system, exploring **interfaces** and **types**, difference between them with various examples to help you master these types.

_Originally published at_ [_https://antondevtips.com_](https://antondevtips.com/blog/typescript-interfaces-vs-types-understanding-the-difference)_._

## What Are Interfaces and Types In TypeScript

**Interfaces** and **types** belong to the [**object types**](https://antondevtips.com/blog/the-complete-guide-to-typescript-types) in TypeScript.

**Object types** in TypeScript groups a set of properties under a single type.

Object types can be defined either using `type` alias:

```typescript
type User = {
    name: string;
  age: number;
  email: string;
};

const user: User = {
  name: "Anton",
  age: 30,
  email: "info@antondevtips.com"
};
```

Or `interface` keyword:

```typescript
interface User {
  name: string;
  age: number;
  email: string;
}

const user: User = {
  name: "Anton",
  age: 30,
  email: "info@antondevtips.com"
};
```

## Interfaces in TypeScript

**Interfaces** are extendable and can inherit from other interfaces using the `extends` keyword:

```typescript
interface Employee extends User {
  employeeId: number;
  salary: number;
}

const employee: Employee = {
  name: "Jack Sparrow",
  age: 40,
  email: "captain.jack@gmail.com",
  employeeId: 1,
  salary: 2000
};
```

> **Classes** in TypeScript can inherit from interfaces and implement them using the `extends` keyword.

You can also extend **interfaces** by declaring the same interface multiple times:

```typescript
interface User {
  name: string;
  age: number;
  email: string;
}

interface User {
  phone: string;
}

const user: User = {
  name: "Anton",
  age: 30,
  email: "info@antondevtips.com",
  phone: "1234567890"
};
```

In TypeScript interfaces, you can use property modifiers to define **optional** and **readonly** properties.

### Optional Properties

These properties are not mandatory when creating an object:

```typescript
interface User {
  name: string;
  age: number;
  email?: string; // Optional property
}

const user: User = {
  name: "Anton",
  age: 30
};
```

### Readonly Properties

These properties cannot be modified after the object is created:

```typescript
interface User {
  name: string;
  readonly age: number;
  email: string;
}

const user: User = {
  name: "Anton",
  age: 30,
  email: "john.doe@example.com"
};

// Error: Cannot assign to 'age' because it is a read-only property.
// user.age = 31;
```

### Index Signatures

Index signatures allow you to define the type of keys and values that an object can have.

Sometimes you may not know all the names of the properties during compile time, but you do know the shape of the values.
In such use cases you can use an index signature to define the properties:

```typescript
interface Names {
  [index: number]: string;
}

const names: Names = ["Anton", "Jack"];
```

You can also define dictionaries by using index signatures:

```typescript
interface NameDictionary {
  [key: string]: string;
}

const dictionary: NameDictionary = {
  name: "Anton",
  email: "info@antondevtips.com"
};
```

There is a limitation when working with index signatures: you can't have named properties of another type:

```typescript
interface NumberDictionary {
  [index: string]: number;
 
  length: number;
  
  // Error: Property 'name' of type 'string' is not assignable to 'string' index type 'number'.
  name: string;
}
```

You can bypass this limitation my using Unions:

```typescript
interface Dictionary {
  [index: string]: number | string;
  length: number;
  name: string;
}

const dictionary: Dictionary = {
    name: "Anton",
    email: "info@antondevtips.com",
    length: 5
};
```

It might be useful to make index signatures readonly to prevent assignment to their indexes:

```typescript
interface ReadonlyDictionary {
  readonly [index: number]: string;
}
 
const dictionary: ReadonlyDictionary = {
    0: "Anton"
};

dictionary[0] = "John";
// Error: Index signature in type 'ReadonlyDictionary' is readonly.
```

## Types in TypeScript

The **type** in TypeScript is similar to the interfaces but you can't extend existing types or add new properties to them.

```typescript
type User = {
  name: string;
  age: number;
  email: string;
};

type User = {
  phone: string;
};

// Error: Duplicate identifier 'User'.
```


## Combining Interfaces and Types with Intersection Types

**Intersection types** in TypeScript allow you to combine multiple types into one.
An **intersection type** can be created by combining multiple interfaces or types using `&` operator:

```typescript
interface Person {
    name: string;
}

interface ContactDetails {
    email: string;
    phone: string;
}

type Customer = Person & ContactDetails;

const customer: Customer = {
    name: "Anton",
    email: "info@antondevtips.com",
    phone: "1234567890"
};
```

In these examples a `Customer` type is an **intersection type** that combines all properties from `Person` and `ContactDetails` interfaces.

It's important to note that you can only declare an **Intersection Type** with a **type** keyword.
You can't declare an interface type that holds an Intersection.

Intersections are not limited just to interfaces and types, they can be used with other types, including primitives, unions, and other intersections.

## Combining Interfaces and Types with Union Types

**Union type** in TypeScript is a type that is formed from two or more other types.
Union types are also called [**Discriminated Unions**](https://antondevtips.com/blog/mastering-discriminated-unions-in-typescript).

A variable of **union type** can have one of the types from the union.

Let's explore an example of geometrics shapes, that have common and different properties:

```typescript
interface Circle {
    type: "circle";
    radius: number;
}

interface Square {
    type: "square";
    square: string;
}

type Shape = Circle | Square;
```

Let's create a function that calculates a square for each shape:

```typescript
function getSquare(shape: Shape) {
    if (shape.type === "circle") {
        return Math.PI * shape.radius * shape.radius;
    }

    if (shape.type === "square") {
        return shape.square;
    }
}

const circle: Circle = {
    type: "circle",
    radius: 10
};

const square: Square = {
    type: "square",
    size: 5
};

console.log("Circle square: ", getSquare(circle));
console.log("Circle square: ", getSquare(square));
```

Here a `getSquare` function accepts a shape parameter that can be one of 2 types: `Circle` or `Square`.
We need to define a property that will allow to distinguish types from each other.
In our example it's a `type` property.

It's important to note that you can only create a **Union Type** with a **type** keyword.
You can't declare an interface type that holds a Union Type.


## Difference Between Interfaces and Types

**Interfaces:**
* can inherit from other interfaces
* can merge declarations, which is useful for extending existing objects
* can't represent more complex structures, like unions and intersections

**Types:**
* doesn't support inheritance
* doesn't allow extending existing objects
* can represent more complex structures, like unions and intersections

## Summary

Both **Interfaces** and **Types** in TypeScript are quite the same but have some differences.
Interfaces allow extension of existing objects, while types - not.
On the other hand Types can represent unions and intersections.

Use **Interfaces** when you need to define the structure of an object and take advantage of declaration merging and extension.
Use **Types** when you need you don't need them to be extended or to define complex types, such as unions or intersections.

There is no right choice whether to prefer Interfaces to Types or vice versa.
You can choose based on the needs and personal preference, the most important part is to select a single approach that will be consistent in the project.
My personal choice is **Types** as they can't be accidentally re-declared and extended, unless I need to do some fancy object-oriented stuff with interfaces and classes.

**P.S.:** you can find an amazing Cheat Sheets on [**Types**](https://www.typescriptlang.org/static/TypeScript%20Types-ae199d69aeecf7d4a2704a528d0fd3f9.png) and [**Interfaces**](https://www.typescriptlang.org/static/TypeScript%20Interfaces-34f1ad12132fb463bd1dfe5b85c5b2e6.png) from the Official TypeScript website.

Hope you find this blog post useful. Happy coding!

_Originally published at_ [_https://antondevtips.com_](https://antondevtips.com/blog/typescript-interfaces-vs-types-understanding-the-difference)_._

### After reading the post consider the following:

- [Subscribe](https://antondevtips.com/blog/typescript-interfaces-vs-types-understanding-the-difference#subscribe) **to receive newsletters with the latest blog posts**
- [Download](https://github.com/AntonMartyniuk-DevTips/dev-tips-code/tree/main/frontend/typescript/interfaces-vs-types) **the source code for this post from my** [github](https://github.com/AntonMartyniuk-DevTips/dev-tips-code/tree/main/frontend/typescript/interfaces-vs-types) (available for my sponsors on BuyMeACoffee and Patreon)

If you like my content —  **consider supporting me**

Unlock exclusive access to the source code from the blog posts by joining my **Patreon** and **Buy Me A Coffee** communities!

[![](https://cdn-images-1.medium.com/max/233/1*GSZFx05RDUWr5pn_E9bK8w.png)](https://www.buymeacoffee.com/antonmartyniuk)

[![](https://cdn-images-1.medium.com/max/229/1*8tJk9eYCJrkzoAJw2cILSA.png)](https://www.patreon.com/bePatron?u=73769486)