TypeScript: Types vs Interfaces vs Classes – Which Should You Use?

If you’re coming from JavaScript, TypeScript’s type system can be daunting. You’ve got type, interface, and class — all of which can describe the shape of your data. But which one should you use? Let’s cut through the noise and get you making the right call, fast.

The three options

If you’re just looking for a quick answer, jump to the summary.


Type: The most abstract

type is the most flexible and simplest option. You can use it for primitives, unions, intersections, functions, and more.

type User = {
  id: UserID;
  name: string;
};
type UserID = string | number;
type Callback = (event: Event) => void;

Interface: The contract

interface is designed for describing the shape of objects. It’s extendable, composable, and can be merged across declarations.

interface User {
  id: UserID;
  name: string;
}

interface Admin extends User {
  permissions: string[];
}

Class: The blueprint

class is a familiar concept for OOP devs. Classes have implementation details, providing both structure (properties) and behavior (methods). They’re overkill for data shapes.

class UserAccount implements User {
  constructor(public id: UserID, public name: string) {}
}

Side-by-Side: When to use each

Use Casetypeinterfaceclass
Object shape
Union/intersection
Declaration merging
Implements/extendsLimited
Runtime code
Functions/tuples/primitives

And finally, my take

Use type almost all the time.

The official TypeScript docs recommend reaching for interface first. They say:

For the most part, you can choose based on personal preference, and TypeScript will tell you if it needs something to be the other kind of declaration. If you would like a heuristic, use interface until you need to use features from type.

I (and many others) disagree. Start with type because it’s simpler. Switch to interface if you need to extend an existing class or want this one to be extendable. And remember, classes are only for when you need to instantiate an instance.