- TypeScript Tutorial
- TypeScript Home
- TypeScript Introduction
- TypeScript Setup
- TypeScript First Program
- TypeScript vs JavaScript
- TypeScript Data Types
- TypeScript Type Inference
- TypeScript Type Annotations
- TypeScript Interfaces
- TypeScript Enums
- TypeScript Type Aliases
- TypeScript Type Assertions
- TypeScript Variables
- TypeScript Functions
- TypeScript Functions
- TypeScript Optional Parameters
- TypeScript Default Parameters
- TypeScript Rest Parameters
- TypeScript Arrow Functions
- Classes and Objects
- Introduction to Classes
- Properties and Methods
- Access Modifiers
- Static Members
- Inheritance
- Abstract Classes
- Interfaces vs Classes
- Advanced Types
- TypeScript Union Types
- TypeScript Intersection Types
- TypeScript Literal Types
- TypeScript Nullable Types
- TypeScript Type Guards
- TypeScript Discriminated Unions
- TypeScript Index Signatures
- TypeScript Generics
- Introduction to Generics
- TypeScript Generic Functions
- TypeScript Generic Classes
- TypeScript Generic Constraints
- TypeScript Modules
- Introduction to Modules
- TypeScript Import and Export
- TypeScript Default Exports
- TypeScript Namespace
- Decorators
- Introduction to Decorators
- TypeScript Class Decorators
- TypeScript Method Decorators
- TypeScript Property Decorators
- TypeScript Parameter Decorators
- Configuration
- TypeScript tsconfig.json File
- TypeScript Compiler Options
- TypeScript Strict Mode
- TypeScript Watch Mode
Interfaces vs Classes in TypeScript
In TypeScript, both interfaces and classes are used to define the structure of an object or blueprint for objects, but they serve different purposes and have different behaviors. Understanding when to use an interface and when to use a class is essential to writing clean, maintainable, and efficient TypeScript code. While they might look similar at first glance, one is a "virtual" contract used during development, while the other is a concrete "factory" for creating objects at runtime.
Key Differences Between Interfaces and Classes
Definition of Structure vs Implementation
- Interface: Defines a contract or structure for objects without providing any implementation details. It defines the shape or blueprint of an object, ensuring that the object adheres to specific rules (properties and methods). It tells TypeScript: "Any object that calls itself a 'User' must have these specific fields."
- Class: Provides both the structure and the implementation. It defines the blueprint for creating objects and can include implementation of methods, as well as data members (properties). It doesn't just describe the object; it provides the logic for how that object behaves.
Instantiating
- Interface: Cannot be instantiated. An interface only defines the structure but does not create objects. Since interfaces are removed during the compilation process, they don't exist in the final JavaScript code.
- Class: Can be instantiated to create objects using the
newkeyword. A class defines both the structure and the implementation, and you can create instances (objects) of a class that persist at runtime.
instanceof with an interface at runtime. If you need to check types at runtime, use a class.
Method Implementation
- Interface: Only defines method signatures (the name, parameters, and return type) but does not provide an implementation. Methods defined in an interface need to be implemented by the class that implements the interface.
- Class: Can define methods with full implementation. Classes can have both abstract methods (unimplemented, meant for subclasses) and concrete methods (fully functional logic).
Inheritance
- Interface: Can extend multiple interfaces, which allows a type to inherit from several sources. This is powerful for creating complex, reusable data shapes. Interfaces are purely structural and are used for type checking.
- Class: Can extend only one other class (single inheritance) to inherit both its properties and methods. However, a class can implement multiple interfaces at the same time.
Support for Properties and Methods
- Interface: Defines only the shape of properties and methods. It doesn't contain any implementation logic. It also cannot use access modifiers like
privateorprotectedon properties (everything is implicitly public). - Class: Contains both the definition (properties) and implementation (methods) of an object. A class can also have constructors, which define how the object is created, and use access modifiers to hide data.
Use Case
- Interface: Primarily used to define types, especially when you need to ensure that an object conforms to a certain shape or structure, or when working with types that will be implemented by different classes. They are "zero-cost" because they don't add size to your final JS file.
- Class: Used to define both the structure and the behavior of objects. Classes are used when you need to create instances, manage internal state, or provide method logic for objects that will be reused throughout your app.
Example Comparison
Interface Example
interface Person {
name: string;
age: number;
greet(): void;
}
class Employee implements Person {
constructor(public name: string, public age: number) {}
greet() {
console.log(`Hello, my name is ${this.name} and I am ${this.age} years old.`);
}
}
const employee = new Employee("John", 30);
employee.greet(); // Output: Hello, my name is John and I am 30 years old.
- The
Personinterface defines the structure for objects, including properties (name,age) and a method (greet()). - The
Employeeclass implements thePersoninterface, ensuring that it provides the correct structure and method implementation. This creates a "safety net" for the developer.
Class Example
class Car {
constructor(public brand: string, public model: string) {}
drive() {
console.log(`The ${this.brand} ${this.model} is driving.`);
}
}
const car = new Car("Toyota", "Corolla");
car.drive(); // Output: The Toyota Corolla is driving.
- The
Carclass not only defines properties (brand,model) but also provides the implementation of thedrive()method. - An object of type
Carcan be instantiated, and the methoddrive()can be invoked directly because the logic is bundled with the definition.
constructor to initialize complex values or set default properties automatically when an object is created.
When to Use an Interface
- Type-checking: If you want to enforce that an object or class conforms to a specific shape or structure (like the JSON response from a REST API), you should use an interface.
- Multiple implementations: When different classes may have different implementations (e.g., a
PdfReportand anExcelReport) but should both adhere to the sameReportstructure. - Avoiding implementation: If you only need to define the shape of an object without needing the actual logic, interfaces are more suitable and keep your code lightweight.
Example: Interface for Multiple Implementations
interface Animal {
sound(): void;
}
class Dog implements Animal {
sound() {
console.log("Woof!");
}
}
class Cat implements Animal {
sound() {
console.log("Meow!");
}
}
const dog = new Dog();
dog.sound(); // Output: Woof!
const cat = new Cat();
cat.sound(); // Output: Meow!
Here, both Dog and Cat classes implement the same Animal interface, ensuring that both classes provide a sound() method, but allowing each to decide what that sound actually is.
When to Use a Class
- Instantiating objects: When you need to create multiple instances of an object with their own internal state.
- Object creation and behavior: If you need an object that has both data (properties) and functionality (methods) that manipulates that data.
- Inheritance: When you want to share common logic between related objects. For example, a
BaseServiceclass that handles API errors, which is then extended by aUserService.
Example: Class with Inheritance
class Animal {
constructor(public name: string) {}
speak() {
console.log(`${this.name} makes a sound`);
}
}
class Dog extends Animal {
speak() {
console.log(`${this.name} barks`);
}
}
const dog = new Dog("Buddy");
dog.speak(); // Output: Buddy barks
Here, the Dog class extends the Animal class and overrides the speak() method. It inherits the name property and the constructor from the parent, saving us from writing repetitive code.
Summary
- Interfaces are used to define structures and types. They disappear after compilation and are best for ensuring that an object or class adheres to a certain contract or shape without adding runtime overhead.
- Classes provide both structure and behavior. They remain in the compiled JavaScript code, can be instantiated, have constructors, and can be extended through inheritance.
When designing your application, use interfaces when you want to define types and structures for data, and use classes when you need to implement object behavior, create instances, and manage application logic.