When working with TypeScript, you’ll often need to choose between interfaces and classes to structure your code. These two features serve different purposes, and knowing when to use each one can make your code more efficient and easier to maintain.
Let’s explore the key differences between TypeScript interfaces and classes, with practical examples you can use in your projects today.
Table of Contents
- Understanding TypeScript Interfaces
- Understanding TypeScript Classes
- Key Differences
- When Should You Use Each?
- Best Practices
- Real-World Example: Building a Task Manager
- Summary
Understanding TypeScript Interfaces
Interfaces are lightweight contracts that define object shapes in TypeScript. They’re perfect for type checking but disappear completely when your code compiles to JavaScript.
interface User {
name: string;
email: string;
age: number;
getFullProfile(): string;
}
// Using an interface
const user: User = {
name: "John Doe",
email: "[email protected]",
age: 30,
getFullProfile() {
return `${this.name} (${this.age})`;
}
};
Code language: JavaScript (javascript)
Understanding TypeScript Classes
Classes are blueprints for creating objects with both data and behavior. Unlike interfaces, classes exist in your final JavaScript code and can create new instances.
class User {
constructor(
public name: string,
public email: string,
public age: number
) {}
getFullProfile(): string {
return `${this.name} (${this.age})`;
}
}
// Creating a new user
const user = new User("John Doe", "[email protected]", 30);
Code language: JavaScript (javascript)
Key Differences
1. Runtime Behavior
Here’s what happens when your code runs:
- Interfaces: They vanish after compilation – no JavaScript output
- Classes: They become real JavaScript constructor functions
2. Implementation
// Interface example
interface Animal {
makeSound(): void;
}
// Class using an interface
class Dog implements Animal {
makeSound(): void {
console.log("Woof!");
}
}
// Abstract class example
abstract class AbstractAnimal {
abstract makeSound(): void;
move(): void {
console.log("Moving...");
}
}
// Class extending abstract class
class Cat extends AbstractAnimal {
makeSound(): void {
console.log("Meow!");
}
}
Code language: JavaScript (javascript)
3. Multiple Inheritance
Interfaces can extend multiple other interfaces:
interface HasName {
name: string;
}
interface HasAge {
age: number;
}
interface Person extends HasName, HasAge {
email: string;
}
Code language: CSS (css)
Classes can extend just one class but implement many interfaces:
class Employee implements HasName, HasAge {
constructor(
public name: string,
public age: number
) {}
}
Code language: PHP (php)
When Should You Use Each?
Pick Interfaces When You Need:
- Simple type checking for objects
- Reusable type definitions
- Working with plain JavaScript objects
- Multiple inheritance
Choose Classes When You Want:
- To create object instances
- Real methods that run in your code
- To use inheritance with concrete implementations
- Private or protected members
Best Practices
Interface Tips
- Keep them focused on one purpose
- Use readonly for properties that shouldn’t change
- Build complex interfaces by extending simpler ones
interface ReadOnlyUser {
readonly id: string;
readonly name: string;
}
interface MutableUser extends ReadOnlyUser {
email: string;
updateEmail(newEmail: string): void;
}
Code language: PHP (php)
Class Tips
- Use public, private, and protected wisely
- Implement interfaces for better type safety
- Don’t create deep inheritance chains
class UserAccount implements MutableUser {
constructor(
public readonly id: string,
public readonly name: string,
private _email: string
) {}
get email(): string {
return this._email;
}
updateEmail(newEmail: string): void {
this._email = newEmail;
}
}
Code language: JavaScript (javascript)
Real-World Example: Building a Task Manager
// Task interface
interface ITask {
id: string;
title: string;
completed: boolean;
toggleComplete(): void;
}
// Task class
class Task implements ITask {
constructor(
public id: string,
public title: string,
public completed: boolean = false
) {}
toggleComplete(): void {
this.completed = !this.completed;
}
}
// TaskList interface
interface ITaskList {
tasks: ITask[];
addTask(task: ITask): void;
removeTask(id: string): void;
}
// TaskList class
class TaskList implements ITaskList {
private _tasks: ITask[] = [];
get tasks(): ITask[] {
return [...this._tasks];
}
addTask(task: ITask): void {
this._tasks.push(task);
}
removeTask(id: string): void {
this._tasks = this._tasks.filter(task => task.id !== id);
}
}
Code language: JavaScript (javascript)
Summary
Interfaces and classes each have their place in TypeScript development. Use interfaces when you just need type checking and want to keep your code light. Pick classes when you need actual objects with behavior in your running code.
Many TypeScript projects use both together – interfaces to define contracts and classes to implement them. This combination gives you both strong typing and practical functionality.
Want to learn more? Check out these related guides:
Remember: There’s no universal right choice between interfaces and classes. Pick the tool that fits your specific needs, and don’t be afraid to use both when it makes sense.