DEV Community

Sachi Goto
Sachi Goto

Posted on • Edited on

Type Aliases VS Interfaces in Typescript

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.

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


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

Enter fullscreen mode Exit fullscreen mode

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.


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!'
    }
}

Enter fullscreen mode Exit fullscreen mode

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" to combine multiple types. Basically, you can use the ampersand (&) to add another type to the alias.

Here is an example


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!'
     } 



Enter fullscreen mode Exit fullscreen mode

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

// 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'
  }


}

Enter fullscreen mode Exit fullscreen mode
  • 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


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)
Enter fullscreen mode Exit fullscreen mode

Difference 3 : Union Types
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


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)
Enter fullscreen mode Exit fullscreen mode

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
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.


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]} 

Enter fullscreen mode Exit fullscreen mode

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

Top comments (0)