Tìm hiểu về utility types trong Typescript
TypeScript cung cấp cho ta một loạt các "Utility Types" (kiểu dữ liệu tiện ích) mạnh mẽ, giúp mình thao tác và biến đổi kiểu dữ liệu dễ dàng. Trong bài viết này, mình sẽ tìm hiểu về hai trong số những loại Utility Types phổ biến nhất: Pick và Omit.
Cả hai Pick và Omit đều cho phép bạn tùy chỉnh kiểu dữ liệu hiện có một cách dễ dàng bằng cách chọn hoặc loại bỏ các thuộc tính cụ thể. Chúng là một phần quan trọng của TypeScript và giúp bạn viết mã nguồn sạch sẽ, quản lý dự án dễ dàng hơn và tăng tính bảo mật trong mã nguồn TypeScript của bạn. Hãy cùng tìm hiểu về chúng và cách chúng có thể được áp dụng trong các tình huống thực tế.
Utility Types là gì?
Utility Types là một tập hợp các kiểu dữ liệu tiện ích được xây dựng sẵn trong TypeScript để giúp bạn tạo, biến đổi và thao tác với kiểu dữ liệu một cách hiệu quả. Các kiểu dữ liệu này giúp bạn tránh việc phải viết mã nguồn thừa và làm cho mã nguồn TypeScript trở nên sạch sẽ, dễ quản lý hơn.
Một số Utility Types phổ biến trong TypeScript bao gồm:
Bài viết này được đăng tại [free tuts .net]
-
Partial<T>
: Cho phép bạn tạo một kiểu dữ liệu mới bằng việc thêm tính năng tùy chọn (optional properties) vào kiểu dữ liệu hiện có. -
Required<T>:
Ngược lại với Partial, Required loại bỏ tất cả tính năng tùy chọn khỏi một kiểu dữ liệu. -
Readonly<T>:
Biến đổi một kiểu dữ liệu bằng cách làm cho tất cả các thuộc tính trở thành thuộc tính chỉ đọc. -
Record<K, T>:
Tạo một kiểu dữ liệu với các thuộc tính được định nghĩa bởi một danh sách khóa K và mỗi thuộc tính có kiểu T. -
Pick<T, K>:
Lấy một phần của kiểu dữ liệu T chỉ bằng cách chọn các thuộc tính có tên nằm trong danh sách khóa K. -
Omit<T, K>:
Tạo một kiểu dữ liệu mới bằng cách loại bỏ các thuộc tính có tên nằm trong danh sách khóa K khỏi kiểu dữ liệu T.
Các Utility Types này giúp bạn viết mã nguồn TypeScript một cách ngắn gọn, tạo ra các kiểu dữ liệu một cách dễ dàng và đảm bảo tính toàn vẹn và an toàn của kiểu dữ liệu trong dự án của bạn.
Tại sao cần Utility Types?
Sử dụng Utility Types mang lại nhiều lợi ích quan trọng cho phát triển ứng dụng TypeScript:
- Tùy chỉnh kiểu dữ liệu: Utility Types cho phép bạn tùy chỉnh kiểu dữ liệu hiện có dễ dàng hơn. Bạn có thể lựa chọn thuộc tính cần thiết hoặc loại bỏ các thuộc tính không mong muốn.
- Tăng tính bảo mật: Việc sử dụng Utility Types giúp kiểm tra kiểu dữ liệu tại thời điểm biên dịch, giảm thiểu lỗi thời gian chạy và tăng tính bảo mật của mã nguồn.
- Tái sử dụng mã nguồn: Utility Types giúp bạn viết mã nguồn tái sử dụng hơn, vì bạn có thể xây dựng các kiểu dữ liệu chung cho nhiều phần của mã nguồn.
Với sự giúp đỡ của Utility Types, bạn có thể nắm vững kiểu dữ liệu trong TypeScript và viết mã nguồn TypeScript một cách hiệu quả và linh hoạt.
Utility Types phổ biến
Trong phần này, mình sẽ tìm hiểu về hai Utility Types phổ biến là Pick<T, K> và Omit<T, K>, và cách chúng có thể được sử dụng để tạo và biến đổi kiểu dữ liệu trong TypeScript.
Pick<T, K>
Định nghĩa và cách sử dụng Pick:
Pick<T, K>
là một Utility Type cho phép bạn tạo một kiểu dữ liệu mới bằng cách chọn một tập hợp các thuộc tính từ kiểu dữ liệu T dựa trên danh sách khóa K.- Ví dụ: Pick<User, 'name' | 'email'> sẽ tạo ra một kiểu dữ liệu mới chỉ chứa thuộc tính 'name' và 'email' từ kiểu dữ liệu User.
Ví dụ minh họa:
type User = { name: string; age: number; email: string; address: string; }; type UserProfile = Pick<User, 'name' | 'email'>; const user: UserProfile = { name: 'John Doe', email: 'john@example.com', };
Omit<T, K>
Định nghĩa và cách sử dụng Omit:
Omit<T, K>
là một Utility Type cho phép bạn tạo một kiểu dữ liệu mới bằng cách loại bỏ một tập hợp các thuộc tính từ kiểu dữ liệu T dựa trên danh sách khóa K.- Ví dụ: Omit<User, 'age' | 'address'> sẽ tạo ra một kiểu dữ liệu mới không chứa thuộc tính 'age' và 'address' từ kiểu dữ liệu User.
Ví dụ minh họa:
type User = { name: string; age: number; email: string; address: string; }; type PublicProfile = Omit<User, 'age' | 'address'>; const user: PublicProfile = { name: 'John Doe', email: 'john@example.com', };
Các Utility Types như Pick và Omit giúp bạn quản lý và biến đổi kiểu dữ liệu một cách hiệu quả trong TypeScript, đặc biệt khi bạn chỉ muốn làm việc với một phần của kiểu dữ liệu mà không cần sao chép toàn bộ kiểu.
Một số utility types khác
Trong TypeScript, ngoài Pick và Omit, còn có một số utility types khác mà bạn có thể sử dụng để tạo và biến đổi kiểu dữ liệu một cách linh hoạt.
Partial<T> - Tạo kiểu dữ liệu với các thuộc tính có thể thiếu
Utility type Partial cho phép bạn tạo một kiểu dữ liệu mới dựa trên một kiểu hiện có, nhưng tất cả các thuộc tính trong kiểu mới có thể bị thiếu (undefined).
interface Todo { title: string; description: string; completed: boolean; } type PartialTodo = Partial<Todo>; const incompleteTodo: PartialTodo = { title: 'Learn TypeScript', };
Readonly<T> - Tạo kiểu dữ liệu chỉ đọc
Utility type Readonly
biến đổi một kiểu dữ liệu hiện có thành kiểu dữ liệu chỉ đọc, tức là bạn không thể thay đổi giá trị của các thuộc tính.
interface Point { x: number; y: number; } type ImmutablePoint = Readonly<Point>; const point: ImmutablePoint = { x: 1, y: 2 }; point.x = 3; // Lỗi: Không thể gán lại giá trị x.
Record<K, T> - Tạo kiểu dữ liệu dựa trên các khóa
Utility type Record
cho phép bạn tạo một kiểu dữ liệu mà mỗi thuộc tính trong đó có kiểu dữ liệu giống nhau và tương ứng với các khóa trong kiểu K.
type Weekdays = 'Monday' | 'Tuesday' | 'Wednesday' | 'Thursday' | 'Friday'; type WeeklySchedule = Record<Weekdays, string>; const schedule: WeeklySchedule = { Monday: 'Work', Tuesday: 'Gym', Wednesday: 'Meetings', // ... };
Required<T> - Tạo kiểu dữ liệu yêu cầu tất cả thuộc tính
Utility type Required
biến đổi một kiểu dữ liệu hiện có thành kiểu dữ liệu yêu cầu tất cả thuộc tính phải được định nghĩa.
interface Config { width?: number; height?: number; } type RequiredConfig = Required<Config>; const fullConfig: RequiredConfig = { width: 800, height: 600, };
NonNullable<T> - Loại bỏ các giá trị null và undefined
Utility type NonNullable
biến đổi kiểu dữ liệu hiện có thành kiểu dữ liệu loại bỏ giá trị null và undefined.
type NullableString = string | null | undefined; type NonNullString = NonNullable<NullableString>; const str: NonNullString = 'Hello, TypeScript!';
Những utility types này giúp bạn tùy chỉnh và biến đổi kiểu dữ liệu một cách dễ dàng trong TypeScript, tạo ra tính linh hoạt và hiệu quả trong việc quản lý kiểu dữ liệu.
Tùy chỉnh utility types
Tạo utility types tùy chỉnh
Trong TypeScript, bạn có thể tạo utility types tùy chỉnh của riêng mình để đáp ứng nhu cầu cụ thể của dự án. Điều này giúp bạn mở rộng tính năng kiểu dữ liệu của TypeScript theo cách linh hoạt và hiệu quả. Dưới đây là một ví dụ đơn giản về cách tạo một utility type tùy chỉnh:
type MyUtilityType<T> = { data: T; timestamp: number; }; const customData: MyUtilityType<string> = { data: 'Hello, TypeScript!', timestamp: Date.now(), };
Trong ví dụ này, MyUtilityType là một utility type tùy chỉnh với một kiểu dữ liệu generics T. Nó tạo ra một kiểu dữ liệu mới với hai thuộc tính: data có kiểu dữ liệu T và timestamp có kiểu dữ liệu number.
Lợi ích và hạn chế của sử dụng utility types
Giúp kiểm tra kiểu dữ liệu
Sử dụng utility types giúp kiểm tra và đảm bảo tính chính xác của kiểu dữ liệu trong mã nguồn TypeScript. Điều này giúp bạn phát hiện và tránh lỗi kiểu dữ liệu trước khi chương trình chạy.
Tăng cường tính bảo mật
Một số utility types như Readonly và Partial giúp bạn tăng cường tính bảo mật bằng cách giới hạn quyền truy cập và sửa đổi đối tượng. Điều này giúp ngăn chặn các lỗi trong quá trình phát triển.
Tái sử dụng mã nguồn
Sử dụng utility types cho phép tái sử dụng mã nguồn và kiểu dữ liệu trong các phần khác nhau của dự án. Điều này giúp giảm sự lặp lại trong mã nguồn và làm cho mã nguồn dễ bảo trì hơn.
Hạn chế
Không phải lúc nào cũng thích hợp
Utility types không phải lúc nào cũng thích hợp cho mọi tình huống. Việc lựa chọn phù hợp giữa sử dụng utility types và viết mã kiểu dữ liệu thủ công phụ thuộc vào yêu cầu cụ thể của dự án.
Không thể thay đổi kiểu dữ liệu tự động
Utility types giúp bạn tạo ra các biến thể mới của kiểu dữ liệu hiện có, nhưng chúng không thay đổi kiểu dữ liệu gốc một cách tự động. Điều này có nghĩa rằng bạn cần sử dụng kiểu biến thể mới thay vì cập nhật kiểu gốc.
Ví dụ minh họa
Sử dụng Pick và Omit trong thực tế
Để hiểu rõ hơn về cách sử dụng Pick và Omit trong các tình huống thực tế, mình hãy xem xét một số ví dụ cụ thể:
Sử dụng Pick để lựa chọn các thuộc tính từ một đối tượng:
interface Person { name: string; age: number; address: string; } type PersonNameAndAge = Pick<Person, 'name' | 'age'>; const personInfo: PersonNameAndAge = { name: 'John', age: 30, };
Trong ví dụ này, mình sử dụng Pick
để lựa chọn chỉ thuộc tính 'name' và 'age' từ kiểu Person, tạo ra kiểu mới PersonNameAndAge
.
Sử dụng Omit để loại bỏ các thuộc tính từ một đối tượng:
interface Car { make: string; model: string; year: number; color: string; } type CarWithoutColor = Omit<Car, 'color'>; const carInfo: CarWithoutColor = { make: 'Toyota', model: 'Camry', year: 2022, };
Ở đây, mình sử dụng Omit để loại bỏ thuộc tính 'color' khỏi kiểu Car, tạo ra kiểu CarWithoutColor
.
Những ví dụ này cho thấy cách Pick và Omit có thể giúp bạn quản lý kiểu dữ liệu một cách hiệu quả trong TypeScript. Chúng giúp bạn tạo ra kiểu dữ liệu mới dựa trên kiểu hiện có, giúp tăng tính bảo mật và tái sử dụng mã nguồn.
Kết bài
Trong bài viết này, mình đã tìm hiểu về utility types trong TypeScript và cách chúng có thể cải thiện tính hiệu quả, tính bảo mật và tái sử dụng mã nguồn trong dự án của bạn. Utility types cung cấp một tập hợp các kiểu dữ liệu tùy chỉnh có sẵn sẽ giúp bạn kiểm tra, bảo vệ kiểu dữ liệu và sáng tạo kiểu dữ liệu mới.
Mình đã tìm hiểu về các utility types phổ biến như Pick, Omit, Partial, Readonly, Record, Required, NonNullable và cách sử dụng chúng để giải quyết các tình huống thực tế.
Mình cũng đã xem xét việc tạo utility types tùy chỉnh và lợi ích của việc sử dụng chúng trong việc kiểm tra kiểu dữ liệu, tăng cường tính bảo mật và tái sử dụng mã nguồn.
Tuy nhiên, mình cũng đã nhắc đến hạn chế của utility types, bao gồm việc chọn lựa giữa sử dụng utility types và viết mã kiểu dữ liệu thủ công theo yêu cầu cụ thể của dự án.
Trong tổng quan, utility types là một công cụ mạnh mẽ cho các nhà phát triển TypeScript để làm cho mã nguồn của họ dễ bảo trì và hiệu quả hơn. Hy vọng rằng sau bài viết này, bạn có thêm hiểu biết về cách sử dụng chúng trong dự án của mình.