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ả interfacetype 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ả interfacetype đề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, interfacetype 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 interfacetype 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 khi interface không thể.


type Status = "success" | "error" | "loading";


  • Intersection Types (Kiểu giao): Cả interfacetype đều có thể sử dụng giao (intersection), nhưng type 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ả interfacetype đề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ả interfacetype đề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ụng declaration 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