Hiểu TypeScript: So Sánh Type và Interface Chi Tiết
TypeScript, một ngôn ngữ mở rộng tĩnh của JavaScript, nâng cao độ tin cậy và khả năng bảo trì mã bằng cách giới thiệu kiểu dữ liệu. Hai khái niệm cơ bản để định nghĩa kiểu dữ liệu trong TypeScript là type aliases và interfaces.
Giới Thiệu
Ban đầu, chúng có vẻ tương tự nhau - cả hai đều mô tả "hình dạng" của dữ liệu. Nhưng khi đi sâu vào, có những khác biệt chính về tính năng, trường hợp sử dụng và hiệu suất. Bài viết này sẽ phân tích rõ ràng những điều này với các giải thích dễ hiểu, ví dụ thực tế và hướng dẫn về việc khi nào nên sử dụng type hay interface.
Type Aliases và Interfaces Là Gì?
Type Aliases
Một type alias là cách đặt tên cho bất kỳ kiểu dữ liệu nào trong TypeScript. Nó có thể đại diện cho các kiểu nguyên thủy, đối tượng, hợp nhất, giao nhau, tuple hoặc các tổ hợp phức tạp.
typescript
// Định nghĩa type alias cho người dùng
type User = {
name: string;
age: number;
};
const user: User = { name: "Alice", age: 25 };
Interfaces
Một interface định nghĩa cấu trúc của một đối tượng hoặc lớp. Nó phù hợp tự nhiên với lập trình hướng đối tượng, hỗ trợ kế thừa và hợp nhất khai báo.
typescript
// Định nghĩa interface cho người dùng
interface User {
name: string;
age: number;
}
const user: User = { name: "Bob", age: 30 };
Dù cả hai đều trông giống nhau, nhưng sự khác biệt của chúng xuất hiện trong các tình huống cụ thể.
Sự Khác Biệt Chính Giữa Type và Interface
1. Tính Linh Hoạt Trong Định Nghĩa Kiểu
- Type Aliases rất linh hoạt:
- Có thể đại diện cho kiểu nguyên thủy, hợp nhất, giao nhau, tuple, kiểu đã ánh xạ và kiểu điều kiện.
typescript
// Một số ví dụ về type alias
type ID = string | number; // Hợp nhất
type Status = "active" | "inactive"; // Kiểu literal
type Point = [number, number]; // Tuple
type User = { name: string } & { age: number }; // Giao nhau
- Interfaces chỉ giới hạn trong việc mô tả hình dạng của đối tượng hoặc hợp đồng của lớp.
typescript
// Định nghĩa interface cho người dùng
interface User {
name: string;
age: number;
}
// ❌ Không thể định nghĩa hợp nhất như string | number
✅ Kết luận: Sử dụng type cho các hợp nhất, kiểu nguyên thủy hoặc các tổ hợp phức tạp. Sử dụng interface cho hình dạng đối tượng/lớp.
2. Khả Năng Mở Rộng
- Interfaces hỗ trợ hợp nhất khai báo và extends, giúp chúng trở thành lựa chọn tuyệt vời cho khả năng mở rộng.
typescript
// Hợp nhất khai báo
interface User {
name: string;
}
interface User {
age: number;
}
const user: User = { name: "Alice", age: 25 };
// Kế thừa
interface Admin extends User {
role: string;
}
- Types không hỗ trợ hợp nhất - bạn phải sử dụng giao nhau.
typescript
// Sử dụng type để kết hợp
type User = { name: string };
type Admin = User & { role: string };
const admin: Admin = { name: "Bob", role: "admin" };
✅ Kết luận: Sử dụng interface khi khả năng mở rộng và hợp nhất là quan trọng.
3. Thực Hiện Lớp
Cả hai đều có thể định nghĩa hợp đồng cho các lớp, nhưng interfaces là lựa chọn chuẩn hơn.
typescript
// Định nghĩa interface cho người dùng
interface User {
name: string;
greet(): string;
}
class Person implements User {
name: string;
constructor(name: string) {
this.name = name;
}
greet() {
return `Hello, ${this.name}!`;
}
}
✅ Kết luận: Ưu tiên interface cho hợp đồng lớp - nó phù hợp hơn với OOP.
4. Cân Nhắc Về Hiệu Suất
- Interfaces: Nhanh hơn trong các dự án lớn nhờ tối ưu hóa tìm kiếm và lưu trữ.
- Types: Có thể tạo thêm chi phí trong các hợp nhất hoặc giao nhau sâu.
✅ Kết luận: Đối với các ứng dụng quy mô lớn, ưu tiên interface cho hình dạng đối tượng.
Ví Dụ Thực Tế
Ví Dụ 1: Kiểu Hợp Nhất Với Type Aliases
typescript
// Định nghĩa kiểu hợp nhất
type ID = string | number;
type Status = "active" | "inactive";
function getUser(id: ID, status: Status) {
return { id, status };
}
👉 Chỉ type hoạt động ở đây - interfaces không thể đại diện cho hợp nhất.
Ví Dụ 2: Hợp Nhất Khai Báo Với Interfaces
typescript
// Hợp nhất khai báo cho đối tượng Window
interface Window {
customProperty: string;
}
interface Window {
customMethod(): void;
}
window.customProperty = "Hello";
window.customMethod = () => console.log("Custom method");
👉 Chỉ interface hỗ trợ hợp nhất - rất tuyệt cho việc mở rộng các kiểu toàn cục.
Ví Dụ 3: Kết Hợp Các Kiểu Với Giao Nhau
typescript
// Kết hợp các kiểu
type Name = { name: string };
type Age = { age: number };
type User = Name & Age;
const user: User = { name: "Alice", age: 25 };
👉 Types làm cho việc kết hợp tạm thời trở nên dễ dàng.
Ví Dụ 4: Các Kiểu Phức Tạp Với Type Aliases
typescript
// Định nghĩa kiểu tùy chọn
type Optional<T> = {
[K in keyof T]?: T[K];
};
type User = { name: string; age: number };
type OptionalUser = Optional<User>;
const user: OptionalUser = { name: "Alice" }; // ✅ Hợp lệ
👉 Các tính năng nâng cao như mapped types chỉ có thể thực hiện với type.
Khi Nào Sử Dụng Type Hay Interface
Sử Dụng interface khi:
- Định nghĩa hình dạng đối tượng hoặc hợp đồng lớp.
- Cần hợp nhất khai báo (ví dụ, mở rộng
Window). - Cần kế thừa với
extends. - Dự án lớn mà hiệu suất biên dịch là quan trọng.
Sử Dụng type khi:
- Định nghĩa hợp nhất, kiểu nguyên thủy hoặc tuple.
- Tạo các tổ hợp kiểu phức tạp với giao nhau.
- Sử dụng mapped hoặc conditional types.
- Muốn định nghĩa kiểu tạm thời nhẹ nhàng.
Những Hiểu Lầm Thông Thường
- “Interfaces chỉ dành cho đối tượng” → Thường đúng, nhưng chúng có thể được mở rộng gián tiếp để linh hoạt hơn.
- “Types kém mạnh mẽ hơn” → Không phải vậy - types linh hoạt hơn trong các trường hợp nâng cao.
- “Bạn phải chọn một” → Sai! Bạn có thể (và nên) sử dụng cả hai khi phù hợp.
Kết Luận
Cả type và interface đều là những công cụ mạnh mẽ trong TypeScript.
- Interfaces xuất sắc trong OOP, kế thừa và hợp nhất khai báo.
- Types tỏa sáng cho hợp nhất, giao nhau và logic kiểu nâng cao.
👉 Một quy tắc hữu ích:
- Sử dụng
interfacecho đối tượng và lớp. - Sử dụng
typecho mọi thứ khác.
Bằng cách hiểu rõ sức mạnh của chúng, bạn sẽ viết mã TypeScript rõ ràng và dễ bảo trì hơn, mở rộng theo quy mô dự án của bạn.