Tiếng Việt

Khám phá kiểu Partial của TypeScript, một tính năng mạnh mẽ để tạo thuộc tính tùy chọn, đơn giản hóa việc thao tác đối tượng và nâng cao khả năng bảo trì mã nguồn với các ví dụ thực tế.

Làm chủ kiểu Partial trong TypeScript: Biến đổi thuộc tính để tăng tính linh hoạt

TypeScript, một tập hợp cha của JavaScript, mang đến kiểu tĩnh cho thế giới phát triển web năng động. Một trong những tính năng mạnh mẽ của nó là kiểu Partial, cho phép bạn tạo ra một kiểu trong đó tất cả các thuộc tính của một kiểu hiện có đều là tùy chọn. Khả năng này mở ra một thế giới linh hoạt khi xử lý dữ liệu, thao tác đối tượng và tương tác API. Bài viết này sẽ khám phá sâu về kiểu Partial, cung cấp các ví dụ thực tế và các phương pháp hay nhất để tận dụng nó một cách hiệu quả trong các dự án TypeScript của bạn.

Kiểu Partial trong TypeScript là gì?

Kiểu Partial<T> là một kiểu tiện ích (utility type) được tích hợp sẵn trong TypeScript. Nó nhận một kiểu T làm đối số chung (generic argument) và trả về một kiểu mới trong đó tất cả các thuộc tính của T đều là tùy chọn (optional). Về cơ bản, nó biến đổi mọi thuộc tính từ bắt buộc (required) thành tùy chọn (optional), nghĩa là chúng không nhất thiết phải có mặt khi bạn tạo một đối tượng thuộc kiểu đó.

Hãy xem xét ví dụ sau:


interface User {
  id: number;
  name: string;
  email: string;
  country: string;
}

const user: User = {
  id: 123,
  name: "Alice",
  email: "alice@example.com",
  country: "USA",
};

Bây giờ, hãy tạo một phiên bản Partial của kiểu User:


type PartialUser = Partial<User>;

const partialUser: PartialUser = {
  name: "Bob",
};

const anotherPartialUser: PartialUser = {
  id: 456,
  email: "bob@example.com",
};

const emptyUser: PartialUser = {}; // Hợp lệ

Trong ví dụ này, PartialUser có các thuộc tính id?, name?, email?, và country?. Điều này có nghĩa là bạn có thể tạo các đối tượng thuộc kiểu PartialUser với bất kỳ sự kết hợp nào của các thuộc tính này, kể cả không có thuộc tính nào. Phép gán emptyUser đã minh họa điều này, làm nổi bật một khía cạnh quan trọng của Partial: nó làm cho tất cả các thuộc tính trở thành tùy chọn.

Tại sao nên sử dụng kiểu Partial?

Kiểu Partial có giá trị trong một số kịch bản sau:

Các ví dụ thực tế về kiểu Partial

1. Cập nhật hồ sơ người dùng

Hãy tưởng tượng bạn có một hàm cập nhật hồ sơ của người dùng. Bạn không muốn yêu cầu hàm phải nhận tất cả các thuộc tính của người dùng mỗi lần gọi; thay vào đó, bạn muốn cho phép cập nhật các trường cụ thể.


interface UserProfile {
  firstName: string;
  lastName: string;
  age: number;
  country: string;
  occupation: string;
}

function updateUserProfile(userId: number, updates: Partial<UserProfile>): void {
  // Mô phỏng việc cập nhật hồ sơ người dùng trong cơ sở dữ liệu
  console.log(`Đang cập nhật người dùng ${userId} với:`, updates);
}

updateUserProfile(1, { firstName: "David" });
updateUserProfile(2, { lastName: "Smith", age: 35 });
updateUserProfile(3, { country: "Canada", occupation: "Software Engineer" });

Trong trường hợp này, Partial<UserProfile> cho phép bạn chỉ truyền vào các thuộc tính cần cập nhật mà không gây ra lỗi kiểu dữ liệu.

2. Xây dựng đối tượng yêu cầu cho một API

Khi thực hiện các yêu cầu API, bạn có thể có các tham số tùy chọn. Việc sử dụng Partial có thể đơn giản hóa việc tạo đối tượng yêu cầu.


interface SearchParams {
  query: string;
  category?: string;
  location?: string;
  page?: number;
  pageSize?: number;
}

function searchItems(params: Partial<SearchParams>): void {
  // Mô phỏng một lệnh gọi API
  console.log("Đang tìm kiếm với các tham số:", params);
}

searchItems({ query: "laptop" });
searchItems({ query: "phone", category: "electronics" });
searchItems({ query: "book", location: "London", page: 2 });

Ở đây, SearchParams định nghĩa các tham số tìm kiếm có thể có. Bằng cách sử dụng Partial<SearchParams>, bạn có thể tạo các đối tượng yêu cầu chỉ với các tham số cần thiết, làm cho hàm trở nên linh hoạt hơn.

3. Tạo một đối tượng Form

Khi xử lý các biểu mẫu (form), đặc biệt là các biểu mẫu nhiều bước, việc sử dụng Partial có thể rất hữu ích. Bạn có thể biểu diễn dữ liệu biểu mẫu dưới dạng một đối tượng Partial và dần dần điền dữ liệu vào đó khi người dùng điền vào biểu mẫu.


interface AddressForm {
  street: string;
  city: string;
  postalCode: string;
  country: string;
}

let form: Partial<AddressForm> = {};

form.street = "123 Main St";
form.city = "Anytown";
form.postalCode = "12345";
form.country = "USA";

console.log("Dữ liệu biểu mẫu:", form);

Cách tiếp cận này hữu ích khi biểu mẫu phức tạp và người dùng có thể không điền tất cả các trường cùng một lúc.

Kết hợp Partial với các kiểu tiện ích khác

Partial có thể được kết hợp với các kiểu tiện ích khác của TypeScript để tạo ra các biến đổi kiểu phức tạp và tùy chỉnh hơn. Một số kết hợp hữu ích bao gồm:

Ví dụ: Partial với Pick

Giả sử bạn chỉ muốn một số thuộc tính nhất định của User là tùy chọn trong quá trình cập nhật. Bạn có thể sử dụng Partial<Pick<User, 'name' | 'email'>>.


interface User {
  id: number;
  name: string;
  email: string;
  country: string;
}


type NameEmailUpdate = Partial<Pick<User, 'name' | 'email'>>;

const update: NameEmailUpdate = {
  name: "Charlie",
  // country không được phép ở đây, chỉ có name và email
};

const update2: NameEmailUpdate = {
  email: "charlie@example.com"
};

Các phương pháp hay nhất khi sử dụng kiểu Partial

Những lưu ý và ví dụ mang tính toàn cầu

Khi làm việc với các ứng dụng toàn cầu, điều cần thiết là phải xem xét cách các kiểu Partial có thể được sử dụng hiệu quả ở các khu vực và bối cảnh văn hóa khác nhau.

Ví dụ: Biểu mẫu địa chỉ quốc tế

Định dạng địa chỉ thay đổi đáng kể giữa các quốc gia. Một số quốc gia yêu cầu các thành phần địa chỉ cụ thể, trong khi những quốc gia khác sử dụng hệ thống mã bưu chính khác nhau. Sử dụng Partial có thể đáp ứng những khác biệt này.


interface InternationalAddress {
  streetAddress: string;
  apartmentNumber?: string; // Tùy chọn ở một số quốc gia
  city: string;
  region?: string; // Tỉnh, tiểu bang, v.v.
  postalCode: string;
  country: string;
  addressFormat?: string; // Để chỉ định định dạng hiển thị dựa trên quốc gia
}


function formatAddress(address: InternationalAddress): string {
  let formattedAddress = "";

  switch (address.addressFormat) {
    case "UK":
      formattedAddress = `${address.streetAddress}\n${address.city}\n${address.postalCode}\n${address.country}`;
      break;
    case "USA":
      formattedAddress = `${address.streetAddress}\n${address.city}, ${address.region} ${address.postalCode}\n${address.country}`;
      break;
    case "Japan":
      formattedAddress = `${address.postalCode}\n${address.region}${address.city}\n${address.streetAddress}\n${address.country}`;
      break;
    default:
      formattedAddress = `${address.streetAddress}\n${address.city}\n${address.postalCode}\n${address.country}`;
  }
  return formattedAddress;
}

const ukAddress: Partial<InternationalAddress> = {
  streetAddress: "10 Downing Street",
  city: "London",
  postalCode: "SW1A 2AA",
  country: "United Kingdom",
  addressFormat: "UK"
};

const usaAddress: Partial<InternationalAddress> = {
    streetAddress: "1600 Pennsylvania Avenue NW",
    city: "Washington",
    region: "DC",
    postalCode: "20500",
    country: "USA",
    addressFormat: "USA"
};

console.log("Địa chỉ Anh:\n", formatAddress(ukAddress as InternationalAddress));
console.log("Địa chỉ Mỹ:\n", formatAddress(usaAddress as InternationalAddress));

Interface InternationalAddress cho phép các trường tùy chọn như apartmentNumberregion để đáp ứng các định dạng địa chỉ khác nhau trên toàn thế giới. Trường addressFormat có thể được sử dụng để tùy chỉnh cách hiển thị địa chỉ dựa trên quốc gia.

Ví dụ: Tùy chọn người dùng ở các khu vực khác nhau

Tùy chọn của người dùng có thể khác nhau giữa các khu vực. Một số tùy chọn có thể chỉ liên quan ở các quốc gia hoặc nền văn hóa cụ thể.


interface UserPreferences {
  darkMode: boolean;
  language: string;
  currency: string;
  timeZone: string;
  pushNotificationsEnabled: boolean;
  smsNotificationsEnabled?: boolean; // Tùy chọn ở một số khu vực
  marketingEmailsEnabled?: boolean;
  regionSpecificPreference?: any; // Tùy chọn linh hoạt theo khu vực
}

function updateUserPreferences(userId: number, preferences: Partial<UserPreferences>): void {
  // Mô phỏng việc cập nhật tùy chọn người dùng trong cơ sở dữ liệu
  console.log(`Đang cập nhật tùy chọn cho người dùng ${userId}:`, preferences);
}


updateUserPreferences(1, {
    darkMode: true,
    language: "en-US",
    currency: "USD",
    timeZone: "America/Los_Angeles"
});


updateUserPreferences(2, {
  darkMode: false,
  language: "fr-CA",
  currency: "CAD",
  timeZone: "America/Toronto",
  smsNotificationsEnabled: true // Bật ở Canada
});

Interface UserPreferences sử dụng các thuộc tính tùy chọn như smsNotificationsEnabledmarketingEmailsEnabled, những thuộc tính này có thể chỉ liên quan ở một số khu vực nhất định. Trường regionSpecificPreference cung cấp sự linh hoạt hơn nữa để thêm các cài đặt dành riêng cho từng khu vực.

Kết luận

Kiểu Partial của TypeScript là một công cụ đa năng để tạo ra mã nguồn linh hoạt và dễ bảo trì. Bằng cách cho phép bạn định nghĩa các thuộc tính tùy chọn, nó đơn giản hóa việc thao tác đối tượng, tương tác API và xử lý dữ liệu. Hiểu cách sử dụng Partial một cách hiệu quả, cùng với sự kết hợp của nó với các kiểu tiện ích khác, có thể nâng cao đáng kể quy trình phát triển TypeScript của bạn. Hãy nhớ sử dụng nó một cách thận trọng, ghi lại mục đích của nó một cách rõ ràng và xác thực dữ liệu để tránh những cạm bẫy tiềm ẩn. Khi phát triển các ứng dụng toàn cầu, hãy xem xét các yêu cầu đa dạng của các khu vực và nền văn hóa khác nhau để tận dụng các kiểu Partial cho các giải pháp có thể thích ứng và thân thiện với người dùng. Bằng cách làm chủ các kiểu Partial, bạn có thể viết mã TypeScript mạnh mẽ hơn, dễ thích ứng hơn và dễ bảo trì hơn, có khả năng xử lý nhiều kịch bản khác nhau một cách tinh tế và chính xác.