
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
- Type: Describes the shape of complex data. Kinda like a label for a set of possible values.
- Interface: Describes the shape of objects. Interfaces are extendable and mergeable.
- Class: Not just a type, but a blueprint for creating objects with both data and behavior.
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;
- Pros: Super flexible, works for anything.
- Cons: Not mergeable, can’t be implemented or extended in the same way as interfaces.
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[];
}
- Pros: Great for OOP patterns, can be extended, merged, and implemented by classes.
- Cons: Only works for object shapes (not unions, primitives, etc).
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) {}
}
- Pros: Encapsulates data and behavior, can implement interfaces.
- Cons: More complex, less abstract than
type
orinterface
.
Side-by-Side: When to use each
Use Case | type | interface | class |
---|---|---|---|
Object shape | ✅ | ✅ | ✅ |
Union/intersection | ✅ | ❌ | ❌ |
Declaration merging | ❌ | ✅ | ❌ |
Implements/extends | Limited | ✅ | ✅ |
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.