DEV Community

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

Posted on

1 1

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)

Playwright CLI Flags Tutorial

5 Playwright CLI Flags That Will Transform Your Testing Workflow

  • 0:56 --last-failed: Zero in on just the tests that failed in your previous run
  • 2:34 --only-changed: Test only the spec files you've modified in git
  • 4:27 --repeat-each: Run tests multiple times to catch flaky behavior before it reaches production
  • 5:15 --forbid-only: Prevent accidental test.only commits from breaking your CI pipeline
  • 5:51 --ui --headed --workers 1: Debug visually with browser windows and sequential test execution

Learn how these powerful command-line options can save you time, strengthen your test suite, and streamline your Playwright testing experience. Click on any timestamp above to jump directly to that section in the tutorial!

Watch Full Video 📹ī¸

Build With Me: AI-Powered Adaptive Web Scraper with LLMs

Join us for a hands-on session with Zia Ahmad where we build an AI-driven web scraper that adapts to site changes in real-time. Code along and level up your automation skills.

Tune in to the full event

DEV is partnering to bring live events to the community. Join us or dismiss this billboard if you're not interested. ❤ī¸