Các loại tiện ích trong TypeScript

Các loại tiện ích trong TypeScript

Bài viết này giải thích các loại tiện ích trong TypeScript.

YouTube Video

Các loại tiện ích trong TypeScript

Các loại tiện ích của TypeScript là những công cụ tiện lợi để tạo ra các kiểu mới dựa trên các kiểu đã có. Điều này cho phép định nghĩa kiểu linh hoạt hơn và tăng khả năng tái sử dụng mã. Tại đây, chúng tôi sẽ giải thích chi tiết các loại tiện ích thường được sử dụng và thảo luận cách sử dụng từng loại với mã mẫu.

Partial<T>

Partial<T> làm cho tất cả thuộc tính của một kiểu đối tượng trở nên tùy chọn (cho phép undefined). Nó hữu ích khi bạn chỉ muốn sử dụng một số thuộc tính mà kiểu gốc có.

 1interface User {
 2  id: number;
 3  name: string;
 4  age: number;
 5}
 6
 7function updateUser(user: Partial<User>) {
 8  console.log(user);
 9}
10
11updateUser({ name: "Alice" }); // Updates only 'name'

Giải thích:

Trong ví dụ trên, việc sử dụng Partial<User> làm cho tất cả thuộc tính của kiểu User trở nên tùy chọn. Do đó, trong hàm updateUser, bạn có thể chỉ truyền một tập hợp con của các thuộc tính.

Required<T>

Required<T> làm cho tất cả thuộc tính, bao gồm các thuộc tính tùy chọn, trở thành bắt buộc. Nó được sử dụng khi bạn muốn chuyển đổi các thuộc tính tùy chọn thành bắt buộc.

 1interface User {
 2  id: number;
 3  name?: string;
 4  age?: number;
 5}
 6
 7function createUser(user: Required<User>) {
 8  console.log(user);
 9}
10
11// createUser({ id: 1 }); // Error: 'name' and 'age' are required
12createUser({ id: 1, name: "Alice", age: 25 });

Giải thích:

Bằng cách sử dụng Required<User>, các thuộc tính như nameage được xem là bắt buộc.

Readonly<T>

Readonly<T> làm cho tất cả thuộc tính của đối tượng chỉ có thể đọc. Điều này ngăn chặn giá trị của đối tượng bị thay đổi.

 1interface User {
 2  id: number;
 3  name: string;
 4}
 5
 6const user: Readonly<User> = {
 7  id: 1,
 8  name: "Alice"
 9};
10
11// user.id = 2; // Error: 'id' is read-only
12console.log(user);

Giải thích:

Bằng cách sử dụng Readonly<T>, bạn có thể bảo vệ các thuộc tính của đối tượng khỏi bị thay đổi. Nó hiệu quả khi bạn muốn ngăn dữ liệu bị thay đổi ngoài ý muốn trong quá trình phát triển.

Record<K, T>

Record<K, T> tạo một kiểu bản đồ với các loại khóa và giá trị được chỉ định. K là loại dành cho khóa (như string hoặc number), và T là loại dành cho giá trị.

 1type Roles = "admin" | "user" | "guest";
 2interface Permissions {
 3  read: boolean;
 4  write: boolean;
 5}
 6
 7const rolePermissions: Record<Roles, Permissions> = {
 8  admin: { read: true, write: true },
 9  user: { read: true, write: false },
10  guest: { read: false, write: false },
11};
12
13console.log(rolePermissions);

Giải thích:

Record<K, T> hữu ích khi bạn muốn định nghĩa cặp khóa-giá trị. Trong ví dụ trên, các quyền được định nghĩa dựa trên vai trò người dùng.

Pick<T, K>

Pick<T, K> chỉ trích xuất các thuộc tính được chỉ định từ một kiểu đối tượng. Bạn có thể tạo một kiểu mới bằng cách chỉ trích xuất các thuộc tính cần thiết.

 1interface User {
 2  id: number;
 3  name: string;
 4  age: number;
 5}
 6
 7type UserSummary = Pick<User, "id" | "name">;
 8
 9const summary: UserSummary = {
10  id: 1,
11  name: "Alice"
12};
13
14console.log(summary);

Giải thích:

Bằng cách sử dụng Pick<T, K>, bạn có thể trích xuất các thuộc tính cụ thể từ một kiểu đối tượng và xem chúng như một kiểu mới. Ví dụ, chỉ có idname được trích xuất trong ví dụ trên.

Omit<T, K>

Omit<T, K> loại bỏ các thuộc tính được chỉ định khỏi một kiểu đối tượng. Đây là thao tác ngược lại của Pick.

 1interface User {
 2  id: number;
 3  name: string;
 4  age: number;
 5  email: string;
 6}
 7
 8type UserWithoutEmail = Omit<User, "email">;
 9
10const userWithoutEmail: UserWithoutEmail = {
11  id: 1,
12  name: "Alice",
13  age: 25
14};
15
16console.log(userWithoutEmail);

Giải thích:

Bằng cách sử dụng Omit<T, K>, bạn có thể tạo một kiểu mới bằng cách loại bỏ các thuộc tính được chỉ định. Trong ví dụ trên, thuộc tính email đã bị loại bỏ.

Exclude<T, U>

Exclude<T, U> tạo ra một kiểu mới bằng cách loại bỏ kiểu U khỏi kiểu hợp T. Nó được sử dụng khi bạn muốn loại bỏ một kiểu cụ thể.

1type Status = "active" | "inactive" | "pending";
2type ExcludedStatus = Exclude<Status, "pending">;
3
4const status: ExcludedStatus = "active"; // "pending" is excluded, so it cannot be chosen
5console.log(status);

Giải thích:

Bằng cách sử dụng Exclude<T, U>, bạn có thể loại bỏ các kiểu không cần thiết trong một kiểu hợp. Trong ví dụ trên, vì "pending" đã bị loại bỏ, chỉ có thể chọn "active" hoặc "inactive".

Extract<T, U>

Extract<T, U> trích xuất các phần khớp với kiểu U bên trong kiểu hợp T. Nó hữu ích khi bạn muốn chỉ trích xuất một kiểu cụ thể.

1type Status = "active" | "inactive" | "pending";
2type ActiveStatus = Extract<Status, "active" | "pending">;
3
4const status: ActiveStatus = "active"; // "inactive" cannot be chosen
5console.log(status);

Giải thích:

Extract<T, U> thực hiện thao tác ngược lại của Exclude. Trong ví dụ trên, chỉ có thể chọn "active""pending".

NonNullable<T>

NonNullable<T> tạo ra một kiểu không bao gồm nullundefined. Nó hữu ích khi bạn muốn loại bỏ các giá trị này khỏi các kiểu tùy chọn.

1type UserName = string | null | undefined;
2type ValidUserName = NonNullable<UserName>;
3
4const name: ValidUserName = "Alice"; // null and undefined cannot be chosen
5console.log(name);

Giải thích:

NonNullable<T> loại bỏ nullundefined khỏi một kiểu chứa chúng. Điều này hữu ích khi bạn muốn đảm bảo rằng một giá trị chắc chắn tồn tại.

Kết luận

Các kiểu tiện ích của TypeScript là công cụ mạnh mẽ để làm cho định nghĩa kiểu trở nên ngắn gọn và linh hoạt hơn. Hiểu và sử dụng đúng cách các kiểu tiện ích cơ bản như Partial, Required, Readonly, Record, v.v., có thể cải thiện tính tái sử dụng và bảo trì mã nguồn. Thành thạo các kiểu này sẽ giúp định nghĩa kiểu mạnh mẽ và an toàn hơn, hỗ trợ phát triển hiệu quả.

Toán tử keyof trong TypeScript

Toán tử keyof trong TypeScript được sử dụng để truy xuất tất cả các tên thuộc tính của một kiểu đối tượng. Sử dụng toán tử này, bạn có thể lấy các khóa của một kiểu đối tượng dưới dạng kiểu hợp nhất. Điều này cực kỳ hữu ích để viết mã an toàn về kiểu dữ liệu.

Cách sử dụng cơ bản

1interface Person {
2    name: string;
3    age: number;
4    email: string;
5}
6
7// Use keyof to get the property names of Person
8type PersonKeys = keyof Person; // "name" | "age" | "email"

Ví dụ sử dụng

  1. Sử dụng nó trong đối số của hàm

    Bạn có thể sử dụng keyof để định nghĩa một hàm có kiểu dựa trên các thuộc tính cụ thể của một đối tượng.

1function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
2    return obj[key];
3}
4
5const person: Person = { name: "Alice", age: 30, email: "alice@example.com" };
6const name = getProperty(person, "name"); // type is string
7const age = getProperty(person, "age");   // type is number
  1. Tăng cường ràng buộc kiểu

    Bằng cách sử dụng keyof, bạn có thể đảm bảo rằng các khóa được truyền vào một hàm được xác minh tại thời điểm biên dịch.

1// Passing an invalid property name results in an error
2const invalid = getProperty(person, "invalidKey"); // Error

Tóm tắt

  • Toán tử keyof được sử dụng để lấy tất cả tên thuộc tính của một kiểu đối tượng.
  • Bạn có thể lấy các tên thuộc tính dưới dạng kiểu hợp nhất, đạt được mã an toàn về kiểu dữ liệu.
  • Bằng cách sử dụng nó trong các đối số của hàm, bạn có thể giới hạn hàm chỉ chấp nhận các tên thuộc tính hợp lệ.

Bằng cách này, toán tử keyof tăng cường an toàn kiểu trong TypeScript và giúp bạn viết mã mạnh mẽ hơn.

Bạn có thể làm theo bài viết trên bằng cách sử dụng Visual Studio Code trên kênh YouTube của chúng tôi. Vui lòng ghé thăm kênh YouTube.

YouTube Video