DEV Community

Cover image for What is the difference between type vs interface in Typescript
Xuanming L.
Xuanming L.

Posted on

What is the difference between type vs interface in Typescript

Common

Both can define a data type.
Types aliases in Typescript mean "a new for any type".

type MyNumber = number;

type StringOrNumber = string | number;

type User = {
  id: number;
  name: string;
  email: string;
}
Enter fullscreen mode Exit fullscreen mode

An interface defines a contract that an object must adhere to.

interface Person { 
  name: string; 
  address: string;
}
Enter fullscreen mode Exit fullscreen mode

Difference

1. Primitive Type

Primitive types are inbuilt types in TypeScripts. They include number, string, boolean, null, and undefined types.
Type alias can be used to define a alias for a primitive type as below.

type MyString = string;
type NullOrUndefined = null | undefined;
Enter fullscreen mode Exit fullscreen mode

But, interface can not be used to define a alias for a primitive type.

2. Union types

Union types can describe values that can be one of several constant and create unions of various primitive, literal or complex types.

type Computer = 'Desktop' | 'Laptop' | 'Tablet';
Enter fullscreen mode Exit fullscreen mode

Union type can only be defined using type. Interface cannot define a union type. But it is possible to create a new union type from several interfaces:

interface Laptop {
  cpu: string;
  ram: number;
  storage: number;
}

interface SmartPhone {
  number: string;
}

type Mobile = Laptop | Phone;
Enter fullscreen mode Exit fullscreen mode

3. Function types

In Typescript, a function type represents a function's prototype. Type alias defines function prototype like this:

type Add = (num1: number, num2: number) => number;
Enter fullscreen mode Exit fullscreen mode

You can also use an interface to do samething:

interface IAdd {
  (num: number, num2: number): number;
}
Enter fullscreen mode Exit fullscreen mode

As you can see both type and interface are similar except for a syntax difference. And type is preferred to define a function prototype.
It is also because type has more capability to define function type. Here's an example:

type Watch = 'Mechanical' | 'Electrical';

type WindUp = (cycle: number) => void;
type Recharge = () => void;

type RefillWatch<W extends Watch> = 
  W extends 'Mechanical' ? 
  WindUp : W extends 'Electrical' ? 
    Recharge : never;

const windUp: RefillWatch<'Mechanical'> = (watch, cycle) => {
  // Something to wind up the spring of the watch
}

const recharge: RefillWatch<'Electrical'> = (watch) => {
  // Something to charge the watch
}
Enter fullscreen mode Exit fullscreen mode

4. Merging of declarations

Merging of declarations is a feature for only interface.

interface Computer {
  cpu: string;
}
interface Computer {
  ram: string;
}

const mine: Computer = {
  cpu: 'Core i-9 9900',
  ram: '32GB',
};
Enter fullscreen mode Exit fullscreen mode

5. Extends and intersection

An interface can extend original interface:

interface Computer {
  cpu: string;
  ram: string;
}

interface Laptop extends Computer {
  battery: string;
}
Enter fullscreen mode Exit fullscreen mode

You can also get same result with type alias:

type Computer = {
  cpu: string;
  ram: string;
}

type Laptop = Computer & {
  battery: string;
}
Enter fullscreen mode Exit fullscreen mode

You can also extends an interface from a type alias:

type Computer = {
  cpu: string;
  ram: string;
}
interface Laptop extends Computer {
  battery: string;
}
Enter fullscreen mode Exit fullscreen mode

But you cannot extend an interface from a union type:

type Watch = 'Mechanical' | 'Electrical';
interface MoreWatch extends Watch {
  brand: string;
}
Enter fullscreen mode Exit fullscreen mode

Type aliases can extend interfaces using the intersection like this:

interface Watch {
  brand: string;
}
Type ElectricWatch = Watch & {
  battery: string;
}
Enter fullscreen mode Exit fullscreen mode

6. Handling conflicts

You cannot extend an interface with same property key like this:

interface Watch {
  refill: () => void;
}
interface Watch {
  refill: (cycle: number) => void;
}
Enter fullscreen mode Exit fullscreen mode

But you can extend a type alias with same property key like this:

type Person = {
  getPermission: (id: string) => string;
};
type Staff = Person & {
   getPermission: (id: string[]) => string[];
};
const AdminStaff: Staff = {
  getPermission: (id: string | string[]) => {
    return (typeof id === 'string' ? 
      'admin' : ['admin']) as string[] & string;
  }
}
Enter fullscreen mode Exit fullscreen mode

If you extend a type alias like this, the model property type would not be determined because it can't be both string and number at the same time:

type Computer = {
  model: string;
};
type Laptop = Computer & {
  model: number;
};
// error: Type 'string' is not assignable to type 'never'.(2322)
const mine: Laptop = { model: 'Dell' };
Enter fullscreen mode Exit fullscreen mode

7. Implementing class

In Typescript, you can implement a class using either an interface or a type alias:

interface Person {
  name: string;
  greet(): void;
}
class Student implements Person {
  name: string;
  greet() {
    console.log('Hello');
  }
}
type Pet = {
  name: string;
  greet(): void;
};
class Cat implements Pet {
  name: string;
  greet() {
    console.log('Mew');
  }
}
Enter fullscreen mode Exit fullscreen mode

But you cannot implement a class that extends a union type:

type Key = { key: number; } | { key: string; };
// can not implement a union type
class PrimaryKey implements Key {
  key = 1
}
Enter fullscreen mode Exit fullscreen mode

8. Tuple type

In Typescript, the tuple type can express an array with a fixed number of elements, while each element has its data type:

type State: [name: string; setter: (value: string) => void];
Enter fullscreen mode Exit fullscreen mode

If you want to declare a tuple type with an interface, you can do it like this:

interface IState extends Array<string | (value: string) => void> {
  0: string;
  1: (value: string) => void;
}
Enter fullscreen mode Exit fullscreen mode

9. Benefits of the type alias than the interface

Here's an example of the advanced type feature that the interface cannot achieve:

type Person = {
    name: string;
    address: string;
}
type Getters<T> = {
    [K in keyof T as `get${Capitalize<string & K>}`]:  () => T[K];
};
type PersonType = Getters<Perso>;
// type PersonType = {
//     getName: () => string;
//     getAddress: () => string;
// }
Enter fullscreen mode Exit fullscreen mode

Conclusion

In this post, I have explained the features of the type alias and the interface in Typescript.
As above the type alias has more powerful features than the interface. So I recommend to use the type alias when coding.

Top comments (0)