Type Aliases vs Interfaces in TypeScript
Tags: beginners, typescript, webdev
Published: 2021-09-29 14:34:18

TypeScript provides two ways to create custom types for our data and they include **Type aliases** and **Interfaces**. This article will discuss the similarities and differences between them, and the best use cases for each. Let's dive in!

## Type aliases

A [type alias](https://devdocs.io/typescript/2/everyday-types#type-aliases) is basically a name for any type. Type aliases can be used to represent not only primitives but also object types, union types, tuples and intersections. Let's take a look at some examples:

```typescript
type userName = string;
type userId = string | number; // Union type
type arr = number[];

// Object type
type Person = {
	id: userId; // We're making use of another type alias here
	name: userName;
	age: number;
	gender: string;
	isWebDev: boolean;
};

const user: Person = {
	id: 12345,
	name: "Tolu",
	age: 1,
	gender: "female",
	isWebDev: true,
};

const numbers: arr = [1, 8, 9];
```

Type aliases are declared with the `type` keyword preceding them. Think of them like regular JavaScript variables that are used to represent certain values. Wherever you use a variable name, it is evaluated to the value that it represents. Type aliases work in a similar manner. Wherever you annotate a type by its alias, the alias will evaluate to the type(s) that it stands for. Like variables, you can't declare the same type alias more than once. Aliases are also never inferred by TypeScript, you have to explicitly annotate them.

## Interfaces

[Interfaces](https://devdocs.io/typescript/2/everyday-types#interfaces) are another way to name data structures e.g objects. The declaration syntax for interfaces is different from that of type aliases. Let's rewrite the type alias `Person` above to an interface declaration:

```typescript
interface Person {
	id: userId;
	name: userName;
	age: number;
	gender: string;
	isWebDev: boolean;
}
```

### Unions and Intersections

A union type is formed from two or more other types, and represents a value that can be any one of them. Intersections allow us to combine multiple existing types into a single type that has all the features of those types. Take the following example:

```typescript
type Human = {
	name: string;
	speaks: boolean;
};

interface Dog {
	name: string;
	barks: boolean;
}

type HumanAndDog = Human & Dog; // Intersection
type HumanOrDogOrBoth = Human | Dog; // Union

let humanAndDog: HumanAndDog = {
	// must have all the properties of Human and Dog
	name: "Sparky",
	speaks: false,
	barks: true,
};

let humanOrDog: HumanOrDogOrBoth = {
	// can have the properties of Human or Dog, or both
	name: "Tolu",
	speaks: true,
};
```

Type aliases and interfaces can be combined into one `type` using unions or intersections, but cannot be combined into an `interface`.

### Tuples

Tuples are a way to type arrays with fixed lengths, accounting for every item in said array.

```typescript
type Mix = [number, string, boolean];

const mix: Mix = [1, "banana", true];
```

In TypeScript, tuples can be declared with types but not interfaces. However, tuples can be used inside interfaces, like this:

```typescript
interface Mix {
	a: [number, string, boolean];
}
```

### Declaration merging

If you declare one `interface` with the same name more than once, TypeScript merges them into one declaration and will treat them as a single interface. This is called **declaration merging**.

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

interface Person {
	age: number;
}

type Num = numbers;
type Num = string; // Duplicate identifier Num

let user: Person = {
	// has the properties of both instances of Person
	name: "Tolu",
	age: 0,
};
```

Declaration merging only works on interfaces. TypeScript will give an error if you declare the same type alias more than once.

### Extends

Both type aliases and interfaces can be extended. However, the syntax differs. A derived type alias or interface has all the properties and methods of its base type alias or interface, and can also define additional members.

A type alias can extend another type alias using an ampersand:

```typescript
type A = { x: number };
type B = A & { y: string };
```

Type aliases can also extend interfaces:

```typescript
interface A {
	x: number;
}

type B = A & { y: string };
```

Interfaces can extend type aliases with the `extends` keyword:

```typescript
type A = {
	x: number;
};

interface B extends A {
	y: string;
}
```

Interfaces can extend other interfaces the same way they extend type aliases. Interfaces can also extend multiple interfaces separated by commas.

```typescript
interface A {
	x: string;
	y: number;
}

interface B {
	z: boolean;
}

interface C extends A, B {
	breed: string;
}

let c: C = {
	x: "Sparky",
	y: 4,
	z: true,
};
```

### Interfaces extending classes

TypeScript allows an interface to extend a [class](https://devdocs.io/typescript/2/classes). When this happens, the interface inherits the members of the base class, including the private and public members, but not their implementations. This means that when you create an interface that extends a class with private members, only that class or its subclasses can implement that interface. This allows you to restrict the use of the interface to the class or subclasses of the class that it extends.

```typescript
class Base {
	greetFriend() {
		console.log(`Hello!`);
	}
}

// Interface extending the Base class
interface Derived extends Base {
	giveGist(): void;
}

// New class that extends Base class and implements the Derived interface
class NewClass extends Base implements Derived {
	giveGist() {
		console.log("I saw this the other day...");
	}
}

const c = new NewClass();
c.greetFriend(); // Hello!
c.giveGist(); // I saw this the other day...
```

### Implements

TypeScript supports class-based object-oriented programming. As a result, it allows classes to implement both type aliases and interfaces using the `implements` keyword. An error will be thrown if a class fails to correctly implement it.

```typescript
// Interface being implemented by a class
interface A {
	x: number;
	y: number;
}

class SomeClass implements A {
	x = 1;
	y = 2;
}

// Type alias being implemented by a class
type B = {
	x: number;
	y: number;
};

class SomeClass2 implements B {
	x = 1;
	y = 2;
}
```

A class can also implement multiple interfaces separated by commas e.g `class A implements B, C {}`. Note that a class can not implement or extend a type alias that represents a union type:

```typescript
type C = { x: number } | { y: number };

// ERROR: A class can only implement an object type or intersection of object types with statically known members.
class SomeClass3 implements C {
	x = 1;
	y = 2;
}
```

> _It’s important to understand that an implements clause is only a check that the class can be treated as the interface type. It doesn’t change the type of the class or its methods at all. A common source of error is to assume that an implements clause will change the class type - it doesn’t!_
> — [TypeScript official docs](https://www.typescriptlang.org/docs/handbook/2/classes.html#class-heritage)

## Which should I use?

Type aliases and interfaces are very similar and you can choose freely between them. Personally, I use type aliases when defining primitive, union, intersection, function or tuple types. However, I make use of interfaces when defining object types or to take advantage of declaration merging.

## Conclusion

We have seen the differences and similarities between type aliases and interfaces.

- Both type aliases and interfaces can be used to describe the shape of an object or a function signature. But the syntax differs.
- Declaration merging only works on interfaces and not type aliases.
- You cannot declare a union, intersection or tuple with the `interface` keyword. However, you can make use of them within an interface.
- Classes can implement both type aliases and interfaces, but not type aliases that represent a union type.

I hope that you have gained something useful from this article. Kindly leave any questions or additional information in the comment section.

Thanks for reading!