Trong TypeScript, cả interface và type đều được sử dụng để xác định cấu trúc dữ liệu, nhưng chúng có những khác biệt quan trọng về cách sử dụng và khả năng mở rộng. Trong bài viết này, chúng ta sẽ so sánh chi tiết hai khái niệm này để hiểu rõ khi nào nên sử dụng interface và khi nào nên chọn type.
1. Giống nhau giữa Interface và Type
Cả interface
và type
trong TypeScript đều có thể được sử dụng để định nghĩa kiểu cho đối tượng, hàm, mảng và nhiều dạng dữ liệu khác. Dưới đây là một ví dụ đơn giản về cách sử dụng cả hai:
// Sử dụng Interface
interface Person {
name: string;
age: number;
}
// Sử dụng Type
type Person = {
name: string;
age: number;
};
const person: Person = { name: "John", age: 30 };
Trong trường hợp này, cả interface
và type
đều thực hiện nhiệm vụ giống nhau, giúp định nghĩa cấu trúc của đối tượng person
.
2. Khác biệt giữa Interface và Type
Mặc dù có thể sử dụng tương tự trong nhiều trường hợp, interface
và type
có một số điểm khác biệt đáng chú ý.
2.1. Khả năng mở rộng (Extensibility)
Một trong những điểm khác biệt chính giữa interface
và type
là cách chúng mở rộng và hợp nhất.
- Interface có thể mở rộng hoặc được mở rộng bởi các interface khác. Interface có tính năng
declaration merging
, tức là nếu có nhiều interface cùng tên, chúng sẽ tự động được hợp nhất.
interface Animal {
name: string;
}
interface Animal {
age: number;
}
// Kết quả: { name: string, age: number }
const cat: Animal = { name: "Tom", age: 5 };
- Type không hỗ trợ "declaration merging", nhưng có thể mở rộng bằng cách sử dụng toán tử
&
(intersection).
type Animal = {
name: string;
};
type Mammal = Animal & {
age: number;
};
const cat: Mammal = { name: "Tom", age: 5 };
2.2. Tính linh hoạt trong định nghĩa (Complex Type)
Type
mạnh mẽ hơn trong việc tạo ra các kiểu phức tạp, ví dụ như sử dụng kết hợp kiểu union, intersection hoặc các template literal types, còn interface
chủ yếu được sử dụng cho cấu trúc đối tượng.
- Union Types (Kiểu hợp):
Type
có thể tạo ra các kiểu hợp, trong khiinterface
không thể.
type Status = "success" | "error" | "loading";
- Intersection Types (Kiểu giao): Cả
interface
vàtype
đều có thể sử dụng giao (intersection), nhưngtype
làm điều này dễ dàng hơn.
type Name = { name: string };
type Age = { age: number };
type Person = Name & Age;
const person: Person = { name: "John", age: 25 };
2.3. Khả năng sử dụng cho kiểu hàm và primitive (Function & Primitive Types)
Type
có khả năng định nghĩa kiểu cho các hàm, kiểu cơ bản (primitive types) hoặc kiểu tuple, điều mà interface
không làm được trực tiếp.
- Định nghĩa kiểu hàm:
// Sử dụng type cho kiểu hàm
type Add = (a: number, b: number) => number;
const add: Add = (a, b) => a + b;
- Định nghĩa tuple:
type Tuple = [number, string];
const example: Tuple = [1, "Hello"];
Ngược lại, interface
chỉ được sử dụng cho đối tượng hoặc class, và không thể định nghĩa trực tiếp các kiểu hàm hoặc tuple.
2.4. Tính kế thừa (Inheritance)
Cả interface
và type
đều có thể kế thừa từ các kiểu khác, nhưng cách thực hiện khác nhau.
- Interface kế thừa interface khác:
interface Shape {
color: string;
}
interface Circle extends Shape {
radius: number;
}
const circle: Circle = { color: "red", radius: 10 };
- Type sử dụng intersection để kế thừa:
type Shape = { color: string };
type Circle = Shape & { radius: number };
const circle: Circle = { color: "red", radius: 10 };
2.5. Tính năng declaration merging của Interface
Một đặc điểm quan trọng của interface
là khả năng declaration merging
, tức là nếu có nhiều khai báo interface
cùng tên, chúng sẽ tự động hợp nhất thành một. Điều này rất hữu ích khi làm việc với các thư viện lớn hoặc mở rộng đối tượng.
interface User {
name: string;
}
interface User {
age: number;
}
// Tự động hợp nhất thành: { name: string; age: number }
const user: User = { name: "Alice", age: 30 };
Ngược lại, type
không hỗ trợ tính năng này, vì vậy nếu định nghĩa hai kiểu type
cùng tên, TypeScript sẽ báo lỗi.
3. Khi nào nên sử dụng Interface và Type?
Khi nên sử dụng interface
:
- Khi cần mở rộng cấu trúc dữ liệu qua nhiều khai báo khác nhau hoặc sử dụng tính năng
declaration merging
.
- Khi định nghĩa cấu trúc của một đối tượng phức tạp hoặc cần tạo ra các API hoặc thư viện có thể dễ dàng mở rộng bởi người dùng khác.
Khi nên sử dụng type
:
- Khi cần làm việc với các kiểu dữ liệu phức tạp như
union
,intersection
, hoặc định nghĩa kiểu hàm và kiểu cơ bản (primitive types).
- Khi cần tạo ra các kiểu dữ liệu có nhiều biến thể, hoặc khi cần kết hợp nhiều kiểu thành một kiểu mới.
4. Kết luận
Cả interface
và type
đều có những lợi ích riêng trong TypeScript. Trong hầu hết các trường hợp, bạn có thể sử dụng bất kỳ cái nào, nhưng hiểu rõ sự khác biệt giữa chúng sẽ giúp bạn lựa chọn đúng công cụ cho từng tình huống cụ thể.
- Sử dụng
interface
khi bạn cần khai báo một cấu trúc có thể mở rộng và có thể sử dụngdeclaration merging
.
- Sử dụng
type
khi bạn cần linh hoạt trong việc kết hợp nhiều kiểu, làm việc với các kiểu cơ bản, hoặc tạo các kiểu union, intersection.
Chọn công cụ phù hợp sẽ giúp code của bạn rõ ràng hơn, dễ maintain và có khả năng scale sau này.
From Gavin Nguyen with love <3
Discussion (undefined)