Blog#55: Sử dụng TypeScript Mapped một cách chuyện nghiệp 😊 (Series: Bón hành TypeScript - PHẦN 5)

image.png

Mình là TUẤN hiện đang là một Full-stack Web Developer tại Tokyo 😊. Nếu bạn thấy Blog này hay xin hãy cho mình một like và đăng ký để ủng hộ mình nhé 😉.

Chào mừng bạn đến với loạt bài Làm chủ TypeScript. Được nằm trong Series BÓN HÀNH TYPESCRIPT, những bài viết này sẽ giới thiệu về kiến ​​thức và kỹ thuật cốt lõi của TypeScript dưới dạng Animations sinh động.

OK GÉT GÔ

Vấn đề

Bạn đã sử dụng Partial, Required, Readonly, and Pick utility types chưa?

image.png

Nếu bạn muốn làm chủ chúng một cách thuần thục và tạo ra các utility types cho riêng mình thì đừng bỏ qua nội dung được đề cập trong bài viết này.

Tạo một type User là một kịch bản phổ biến trong công việc hàng ngày. Ở đây, chúng ta có thể sử dụng TypeScript để xác định loại User trong đó tất cả các khóa được yêu cầu.

type User = {
  name: string; 
  password: string; 
  address: string; 
  phone: string;
};

Thông thường, đối với Type User đã được khai báo, chúng ta chỉ cho phép sửa đổi một số thông tin. Tại thời điểm này, chúng ta có thể xác định một loại UserPartial mới đại diện cho loại đối tượng User cần cập nhật, trong đó tất cả các khóa là tùy chọn.

type UserPartial = {
  name?: string; 
  password?: string; 
  address?: string; 
  phone?: string; 
};

Đối với kịch bản xem thông tin user, chúng ta hy vọng rằng tất cả các khóa trong loại đối tượng tương ứng với đối tượng user đều ở chế độ chỉ đọc (Readonly). Đối với yêu cầu này, chúng ta có thể xác định loại User chỉ đọc.

type ReadonlyUser = {
  readonly name: string;
  readonly password: string;
  readonly address: string;
  readonly phone: string;
};

Xem lại ba Type liên quan đến user đã được xác định, bạn sẽ thấy rằng chúng chứa rất nhiều code trùng lặp.

1_kkDPP22K4ZNHpFbczoZWXQ.gif

image.png

Vậy làm cách nào để có thể giảm bớt code trùng lặp trong các loại trên? Câu trả lời là bạn có thể sử dụng các Mapped Types, là các Type chung có thể được sử dụng để ánh xạ loại đối tượng ban đầu sang loại đối tượng mới.

1_-kLV6wQGC2-ahfgSwHXlsA.gif

image.png

1_TtKLifeeVvG3Us9g_S8DEw.gif

image.png

Mapped Type

Cú pháp cho các loại ánh xạ như sau:

image.png

Trường hợp P in K tương tự như câu lệnh in trong JavaScript, được sử dụng để lặp qua tất cả các loại trong loại K và biến loại T, được sử dụng để biểu thị bất kỳ loại nào trong TypeScript.

1_YK9f_jV3ETabwSDqHUSCmQ.gif

Bạn cũng có thể sử dụng các cú pháp sửa đổi bổ sung chỉ đọc và dấu chấm hỏi (?) trong quá trình mapping. Các cú pháp sửa đổi tương ứng được thêm vào và loại bỏ bằng cách thêm các tiền tố dấu cộng(+) và dấu trừ(-). Mặc định là sử dụng dấu cộng nếu không thêm tiền tố.

Bây giờ chúng ta có thể tóm tắt cú pháp của các loại Mapping phổ biến.

{ [ P in K ] : T }
{ [ P in K ] ?: T }
{ [ P in K ] -?: T }
{ readonly [ P in K ] : T }
{ readonly [ P in K ] ?: T }
{ -readonly [ P in K ] ?: T }

Sau khi xem cú pháp của các loại Mapped Types, giờ hãy đến một số ví dụ.

image.png

Hãy xem cách xác định lại loại UserPartial bằng cách sử dụng các Mapped Types.

type MyPartial<T> = {
  [P in keyof T]?: T[P];
};
type UserPartial = MyPartial<User>;

Trong đoạn code trên, chúng ta xác định Mapped Types MyPartial và sau đó sử dụng nó để ánh xạ loại User thành loại UserPartial. Toán tử keyof được sử dụng để lấy tất cả các khóa của một loại và kiểu trả về của nó là kiểu kết hợp. Biến loại P thay đổi thành một loại khác với mỗi lần duyệt, T[P], tương tự như cú pháp truy cập Properties và được sử dụng để lấy loại value tương ứng với một Properties của loại đối tượng.

Hãy xem ảnh minh họa quy trình thực thi hoàn chỉnh của Mapped Types MyPartial, nếu chưa rõ, bạn có thể xem nhiều lần để hiểu sâu hơn về Mapped Types TypeScript.

1_vWOMJV3WyfaS8C8fqpm33A.gif

image.png

TypeScript 4.1 cho phép chúng ta ánh xạ lại các khóa trong các Mapped Types bằng mệnh đề as. Cú pháp của nó như sau:

type MappedTypeWithNewKeys<T> = {
    [K in keyof T as NewKeyType]: T[K]
    //            ^^^^^^^^^^^^^
    //            New Syntax!
}

Trong đó loại NewKeyType phải là một kiểu con của string | number | symbol union type. Sử dụng mệnh đề as, chúng ta có thể xác định Getters utility type và tạo ra loại Getter tương ứng cho loại đối tượng.

type Getters<T> = {
  [K in keyof T as `get${Capitalize<string & K>}`]: () => T[K]
};
interface Person {
    name: string;
    age: number;
    location: string;
}
type LazyPerson = Getters<Person>;
// {
//   getName: () => string;
//   getAge: () => number;
//   getLocation: () => string;
// }

1_Jc_axCACQuR7yZyr3FvfbQ.gif

image.png

Trong đoạn code trên, vì loại được trả về bởi keyof T có thể chứa kiểu ký hiệu (Symbol type) và kiểu Viết hoa chữ cái đầu (Capitalize utility). Nó yêu cầu kiểu được xử lý cần phải là một kiểu con của loại string, nên cần phải lọc kiểu bằng toán tử &.

Ngoài ra, trong quá trình Mapping lại các Keys, chúng ta có thể lọc các Keys bằng cách trả về never type.

// Remove the 'kind' property
type RemoveKindField<T> = {
    [K in keyof T as Exclude<K, "kind">]: T[K]
};
interface Circle {
    kind: "circle";
    radius: number;
}
type KindlessCircle = RemoveKindField<Circle>;
//   type KindlessCircle = {
//       radius: number;
//   };

1_L4ygMaGCnXdNUk4ZVlJ9nA.gif

image.png

Sau khi đọc bài viết này, mình chắc rằng bạn đã hiểu chức năng của các mapped types và cách implement một số utility types bên trong TypeScript.

Mapping là một trong những kiến thức nền tảng và cốt lõi để bạn có thể tiến xa hơn trên con đường chinh phục những khái niệm nâng cao khác trong Typescript.

Roundup

Như mọi khi, mình hy vọng bạn thích bài viết này và học thêm được điều gì đó mới.

Cảm ơn và hẹn gặp lại các bạn trong những bài viết tiếp theo! 😍

Nếu bạn thấy Blog này hay xin hãy cho mình một like và đăng ký để ủng hộ mình nhé. Thank you.😉

NGUYỄN ANH TUẤN

Xin chào, mình là Tuấn, một kỹ sư phần mềm đang làm việc tại Tokyo. Đây là blog cá nhân nơi mình chia sẻ kiến thức và kinh nghiệm trong quá trình phát triển bản thân. Hy vọng blog sẽ là nguồn cảm hứng và động lực cho các bạn. Hãy cùng mình học hỏi và trưởng thành mỗi ngày nhé!

Đăng nhận xét

Mới hơn Cũ hơn