What is the difference between type vs interface in Typescript

RMAG news

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

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

interface Person {
name: string;
address: string;
}

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;

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;

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;

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;

You can also use an interface to do samething:

interface IAdd {
(num: number, num2: number): number;
}

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
}

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,
};

5. Extends and intersection

An interface can extend original interface:

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

interface Laptop extends Computer {
battery: string;
}

You can also get same result with type alias:

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

type Laptop = Computer & {
battery: string;
}

You can also extends an interface from a type alias:

type Computer = {
cpu: string;
ram: string;
}
interface Laptop extends Computer {
battery: string;
}

But you cannot extend an interface from a union type:

type Watch = Mechanical | Electrical;
interface MoreWatch extends Watch {
brand: string;
}

Type aliases can extend interfaces using the intersection like this:

interface Watch {
brand: string;
}
Type ElectricWatch = Watch & {
battery: string;
}

6. Handling conflicts

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

interface Watch {
refill: () => void;
}
interface Watch {
refill: (cycle: number) => void;
}

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

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

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);
}
}

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
}

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

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

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

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.