Interface trong TypeScript là một cấu trúc được sử dụng để định nghĩa các kiểu dữ liệu cho các đối tượng. Nó không cung cấp cách hiện thực (implementation) như các lớp (class) mà chỉ đóng vai trò là một “hợp đồng” đảm bảo rằng bất kỳ đối tượng hoặc lớp nào tuân theo interface đó đều phải có các thuộc tính và phương thức được định nghĩa trong nó.
Interface giúp cung cấp sự kiểm tra kiểu mạnh mẽ hơn, làm cho mã dễ đọc và dễ bảo trì hơn. Thông qua việc định nghĩa các thuộc tính và phương thức, interface không chỉ làm rõ cấu trúc của đối tượng mà còn đảm bảo rằng mọi đối tượng hoặc lớp sử dụng interface đều tuân thủ một giao diện nhất quán.
Định nghĩa Interface trong TypeScript
Interface là gì?
Trong TypeScript, interface là một cách để định nghĩa các thuộc tính và phương thức mà một đối tượng hoặc lớp phải tuân theo. Nó giúp kiểm tra kiểu dữ liệu và hỗ trợ lập trình hướng đối tượng, nhưng không chứa các phương thức hiện thực. Interface thường được dùng để mô tả các đối tượng phức tạp, đồng thời kiểm tra tính nhất quán của chúng trong toàn bộ mã nguồn.
interface Person {
name: string;
age: number;
speak(): void;
}
Trong ví dụ trên, interface Person
yêu cầu bất kỳ đối tượng nào tuân theo nó đều phải có các thuộc tính name
, age
, và phương thức speak
.
Sử dụng Interface để định nghĩa cấu trúc của đối tượng
Bạn có thể sử dụng interface để đảm bảo rằng một đối tượng có đầy đủ các thuộc tính như đã định nghĩa. Khi tạo ra một đối tượng tuân theo interface, TypeScript sẽ kiểm tra tính hợp lệ của đối tượng đó, đảm bảo rằng tất cả các thuộc tính đều tồn tại và có đúng kiểu dữ liệu.
interface Car {
make: string;
model: string;
year: number;
}
let myCar: Car = {
make: "Toyota",
model: "Corolla",
year: 2022
};
Nếu bạn cố gắng tạo một đối tượng không tuân theo cấu trúc interface, TypeScript sẽ báo lỗi ngay lập tức:
let anotherCar: Car = {
make: "Honda",
model: "Civic"
// TypeScript sẽ báo lỗi vì thiếu thuộc tính 'year'
};
Interface và kiểu tùy chọn (Optional Properties)
Một trong những tính năng mạnh mẽ của interface là khả năng định nghĩa các thuộc tính tùy chọn. Bạn có thể cho phép một số thuộc tính có thể có hoặc không trong một đối tượng bằng cách sử dụng dấu chấm hỏi ?
sau tên thuộc tính.
interface Employee {
name: string;
position: string;
age?: number; // age là thuộc tính tùy chọn
}
let employee1: Employee = {
name: "Alice",
position: "Developer"
};
let employee2: Employee = {
name: "Bob",
position: "Manager",
age: 40
};
Trong ví dụ trên, age
là thuộc tính tùy chọn, nên đối tượng employee1
có thể không có age
mà vẫn hợp lệ.
Interface và phương thức
Ngoài việc định nghĩa các thuộc tính, interface cũng có thể định nghĩa phương thức. Phương thức trong interface là một mô tả về chức năng mà các lớp hoặc đối tượng sử dụng interface đó cần phải hiện thực.
interface Animal {
name: string;
move(distance: number): void;
}
class Dog implements Animal {
name: string;
constructor(name: string) {
this.name = name;
}
move(distance: number): void {
console.log(`${this.name} moved ${distance} meters.`);
}
}
let myDog = new Dog("Buddy");
myDog.move(10); // Buddy moved 10 meters.
Trong ví dụ trên, lớp Dog
phải hiện thực phương thức move
để tuân theo giao diện Animal
. TypeScript sẽ báo lỗi nếu phương thức này không được hiện thực hoặc được hiện thực sai cách.
Interface và kế thừa
Giống như các lớp, interface trong TypeScript cũng có thể kế thừa từ một hoặc nhiều interface khác, giúp mở rộng và tái sử dụng các giao diện đã có mà không cần phải định nghĩa lại từ đầu.
Kế thừa đơn
interface Shape {
color: string;
}
interface Square extends Shape {
sideLength: number;
}
let square: Square = {
color: "blue",
sideLength: 10
};
Trong ví dụ trên, Square
kế thừa từ Shape
, vì vậy nó phải bao gồm cả thuộc tính color
và sideLength
.
Kế thừa nhiều interface
TypeScript cũng hỗ trợ kế thừa nhiều interface, cho phép một interface mở rộng nhiều giao diện khác nhau.
interface Pen {
write(): void;
}
interface Eraser {
erase(): void;
}
interface Marker extends Pen, Eraser {}
class WhiteboardMarker implements Marker {
write(): void {
console.log("Writing with marker");
}
erase(): void {
console.log("Erasing with marker");
}
}
let marker = new WhiteboardMarker();
marker.write(); // Writing with marker
marker.erase(); // Erasing with marker
Ở đây, Marker
kế thừa cả hai interface Pen
và Eraser
, yêu cầu lớp WhiteboardMarker
phải hiện thực cả hai phương thức write
và erase
.
Interface và lớp trừu tượng (Abstract Class)
Mặc dù interface và lớp trừu tượng (abstract class) có nhiều điểm tương đồng, nhưng chúng có một số khác biệt. Lớp trừu tượng có thể chứa cả các phương thức hiện thực và không hiện thực, trong khi interface chỉ mô tả các phương thức và thuộc tính mà không có hiện thực.
- Interface chỉ mô tả cấu trúc, không thể có bất kỳ logic nào.
- Lớp trừu tượng có thể có logic (các phương thức đã hiện thực).
Nếu bạn muốn chia sẻ code logic giữa các lớp kế thừa, bạn nên sử dụng lớp trừu tượng thay vì interface.
abstract class Animal {
abstract makeSound(): void;
move(): void {
console.log("Moving...");
}
}
class Dog extends Animal {
makeSound(): void {
console.log("Woof! Woof!");
}
}
let dog = new Dog();
dog.makeSound(); // Woof! Woof!
dog.move(); // Moving...
Interface cho các hàm
TypeScript còn hỗ trợ định nghĩa interface cho các hàm. Điều này rất hữu ích khi bạn muốn đảm bảo rằng một hàm phải tuân theo một giao diện nhất định, với các tham số và kiểu trả về cụ thể.
interface MathOperation {
(a: number, b: number): number;
}
let add: MathOperation = (x: number, y: number): number => {
return x + y;
};
console.log(add(5, 3)); // 8
Trong ví dụ trên, interface MathOperation
yêu cầu bất kỳ hàm nào tuân theo nó phải nhận hai tham số kiểu number
và trả về một giá trị kiểu number
.
Kết luận
Interface trong TypeScript là một công cụ mạnh mẽ giúp đảm bảo tính nhất quán và kiểm soát kiểu dữ liệu trong mã nguồn. Với khả năng định nghĩa rõ ràng các thuộc tính và phương thức, interface giúp cải thiện tính an toàn, hỗ trợ lập trình hướng đối tượng và dễ dàng duy trì mã. Dù không chứa logic như lớp trừu tượng, nhưng interface đóng vai trò quan trọng trong việc tổ chức và quản lý các đối tượng trong TypeScript, giúp lập trình viên xây dựng mã nguồn sạch, rõ ràng và linh hoạt.