Type Aliases VS Interfaces in Typescript
Tags: typescript
Published: 2023-03-26 10:16:23

Type aliases and interfaces are similar in a way that both are used to define **object shapes**. However, they have some notable differences that make them suitable for different scenarios. Understanding these differences can help you make decisions on when to use each one!
 
Here is an example of an interface and a type alias.

``` typescript
// Interface
interface Profile{
       name:string;
       age:number;
       method(): string;


// Type Aliases
type Profile = {
       name:string;
       age:number;
       method(): string;
}

```
They look almost identical. So What are the differences? 

Difference 1 : **extends** vs **intersection**
Similar to how the extends keyword is used in class declarations in object-oriented programming, interfaces can **extends** from another interface using the "extends" keyword.

This is how it works. 
``` typescript

interface Profile{
       name:string;
       email:string;
       age:number;  
}

interface MoreProfile extends Profile{
   phone:number;
   greeting(): string;
} 

const steve:MoreProfile ={
    name:'Steve',
    email:'steve@gmail.com',
    age:20,
    phone:123,
    greeting(){
          return  'Hello!'
    }
}

```

The MoreProfile interface inherits from the Profile interface. It means that if you use the MoreProfile interface for a variable, you will need to include all the properties from both the Profile and MoreProfile interfaces. This feature is useful and intuitive, as the extends keyword clearly signifies hierarchical relationships between interfaces. Neat, right?

**How about Type Aliases? **
 
Type aliases have a similar feature to extends called "[intersection](https://www.typescripttutorial.net/typescript-tutorial/typescript-intersection-types/)" to combine multiple types. Basically, you can use the ampersand (&) to add another type to the alias.

Here is an example

```TypeScript

type Profile = {
       name:string;
       email:string;
       age:number;     
}

type MoreProfile =  {
   phone:number;
   greeting(): string;
} 

const steve:Profile & MoreProfile ={
    name:'Steve',
    email:'steve@gmail.com',
    age:20,
     phone:123,
     greeting(){
          return  'Hello!'
     } 



```

Also, you can mix and match interfaces and type aliases. 

```TypeScript
// adding a type alias to interface

type Profile = {
       name:string;
       email:string;
       age:number;  
}

interface MoreProfile extends Profile  {
    phone:number;
   greeting(): string;
}


const steve:MoreProfile = {
  name:'steve',
  email:'steve@gmail.com',
  age:20,
  phone:123,
  greeting(){
    return 'hello'
  }

}

// Adding interface to a type aliase 

interface Profile {
       name:string;
       email:string;
       age:number;  
}


type MoreProfile = Profile & {
    phone:number;
   greeting(): string;
}


const steve:MoreProfile = {
  name:'steve',
  email:'steve@gmail.com',
  age:20,
  phone:123,
  greeting(){
    return 'hello'
  }


}

```

* Keep in mind that interfaces cannot extend type aliases with union types, as I will explain later in the third difference: Union Types

I don't recommend mixing and matching interfaces and type aliases. It's better to maintain consistency and use one approach throughout your codebase to avoid confusion and inconsistency.


**Difference 2 : Interface Merging**

Interface merging is a unique feature that type aliases do not have. However, it is not recommended to rely heavily on this feature as it can make the code harder to maintain and understand.

One use case of interface merging is to create an interface that describes the entire API. This can be achieved by creating an empty api interface at the parent level and adding the api interface that describes the API structure in its child components/modules. This approach ensures that each module is responsible for creating its own API interface, which promotes better organization and modularity in the codebase.

Here is how it works

```TypeScript

interface Profile{
       name:string;
 
}

interface Profile{
  email:string;
}

const steve:Profile = {
    name:'steve',
    email:'steve@gmail.com'

}

// if email is missing, it will throw you an error:
// Property 'email' is missing in type '{ name: string; }' but required in type 'Profile'.(2741)

// You can not do this with type aliases 

type Profile = {
   name:string;
}

type Profile ={
   email:string;
}

//error Duplicate identifier 'Profile'.(2300)

const steve:Profile = {
    name:'steve',
    email:'steve@gmail.com'

}

//error Type '{ name: string; email: string; }' is not assignable to type 'Profile'.
  Object literal may only specify known properties, and 'email' does not exist in type 'Profile'.(2322)
```


**Difference 3 : [Union Types](https://www.tutorialsteacher.com/typescript/typescript-union
)**
Interfaces do not accept union types, while type aliases do. This is because interfaces can only accept statically known members, meaning that they cannot accept objects that may potentially change.

In other words, interfaces require all properties to be explicitly defined at the time of declaration, while type aliases allow for more flexibility by accepting union types and other dynamic properties.

Here is the example

```TypeScript

type Profile = {name:string} | {name:number}

interface MoreProfile extends Profile  {
    phone:number;
   greeting(): string;
}

// error An interface can only extend an object type or intersection of object types with statically known members.(2312)
```

In this example, since Profile is union type, the shape of the Profile object can be wither name:string or name:number which means it's not static so it will throw you an error! 

If you use union types a lot in your code base, it would be better to use types because interface won't accept union types. 

**Difference 4 [Tuples](https://www.tutorialsteacher.com/typescript/typescript-tuple)**
Tuples can be declared in Type Aliases but for interfaces, you can't unless you declare a tuple inside an interface. 

Here is the example below. 
``` typescript

type Profile = [name:string, age:number]
const typeAliaseProfile:Profile = ['Steve', 20] 

// declare tuples only inside of the interface 
interface Profile { profile:[string, number]}
const interfaceProfile:Profile = {profile:['Steve', 20]} 

```

## Summary
Interfaces and type aliases both used to defined the shape of an object. Both take option using a question mark and readonly features. 

Interface only - Interface merging and extends 

Type Aliases only - Union types, Tuple (interface takes tuples only within an interface), intersection


## Conclusion
If you are familiar with OOP, using interface might be intuitive for you. If you use union types often in your base code, using type aliases make sense. 
Whichever you choose, it's important to be consistent. 

## Resources 

- [Learning TypeScript by Josh Goldberg](https://www.amazon.com/Learning-TypeScript-Development-Type-Safe-JavaScript/dp/1098110331)

- [DIFFERENCE BETWEEN TYPES AND INTERFACE by Girish Gupta](https://www.hellogirish.com/blog/difference-between-types-and-interface)

- [Interfaces vs Type Aliases: what's the difference? by Andrew Burgess](https://www.youtube.com/watch?v=QYO-sieqLD4)