+1

Utility Types trong TS | TypeScript Series

Utility Types là gì?

Utility types là những kiểu được dựng sẵn trong TypeScript để giúp biến đổi, chuyển đổi các kiểu dữ liệu khác dễ dàng hơn — thay vì viết lại toàn bộ kiểu từ đầu.

image.png

Các Utility Types phổ biến & cách dùng

1. Partial<T>

Khi bạn dùng Partial<T> với kiểu T, mọi thuộc tính của T sẽ trở thành tùy chọn (optional).

Ví dụ:

interface Point {
  x: number;
  y: number;
}
let pointPart: Partial<Point> = {}; // x và y đều không bắt buộc
pointPart.x = 10;

Khi dùng: Khi bạn chỉ cần cung cấp một phần dữ liệu — ví dụ cập nhật một phần object, hoặc constructor nhận dữ liệu “rời rạc”.

2. Required<T>

Ngược lại với Partial<T>, Required<T> biến mọi thuộc tính của T thành bắt buộc (required) — kể cả những thuộc tính vốn có dấu ?.

Ví dụ:

interface Car {
  make: string;
  model: string;
  mileage?: number;
}
let myCar: Required<Car> = {
  make: 'Ford',
  model: 'Focus',
  mileage: 12000 // mileage giờ bắt buộc có
};

Khi dùng: Khi bạn muốn đảm bảo rằng object nào đó “hoàn chỉnh” — không thiếu bất cứ thuộc tính nào.

3. Record<K, T>

Record<K, T> tạo một kiểu object mà key của nó nằm trong kiểu Kvalue của mỗi key nằm trong kiểu T.

Ví dụ:

const nameAgeMap: Record<string, number> = {
  'Alice': 21,
  'Bob': 25
};
// Tương đương với: { [key: string]: number }

Khi dùng: Khi bạn muốn tạo map/dictionary với key và value kiểu rõ ràng — ví dụ mapping tên người → tuổi, id → data object, v.v.

4. Omit<T, K>

Omit<T, K> tạo kiểu mới từ T nhưng loại bỏ đi những thuộc tính trong K (một hoặc nhiều key).

Ví dụ:

interface Person {
  name: string;
  age: number;
  location?: string;
}
const bob: Omit<Person, 'age' | 'location'> = {
  name: 'Bob'
  // age và location không được khai!
};

Khi dùng: Khi bạn có interface lớn nhưng trong ngữ cảnh nào đó chỉ muốn sử dụng “một phần” của nó và loại bỏ những trường không cần.

5. Pick<T, K>

Tạo một kiểu mới chỉ bao gồm một số thuộc tính (được chọn) từ kiểu gốc T.

Cú pháp:

Pick<Type, Keys>
  • Type: kiểu gốc
  • Keys: tên thuộc tính (hoặc tập hợp tên) bạn muốn giữ lại

Ví dụ:

interface Person {
  name: string;
  age: number;
  location: string;
}

type PersonNameAndAge = Pick<Person, "name" | "age">;

const user: PersonNameAndAge = {
  name: "Alice",
  age: 25
  // location bị loại bỏ
};

Khi dùng: Khi bạn chỉ muốn lấy một vài thuộc tính cụ thể từ một kiểu lớn hơn — ví dụ chỉ lấy “name” và “age” từ Person.

6. Readonly<T>

Biến tất cả thuộc tính của T thành chỉ đọc (không thể gán lại giá trị).

Cú pháp:

Readonly<Type>

Ví dụ:

interface Book {
  title: string;
  author: string;
}

const myBook: Readonly<Book> = {
  title: "Atomic Habits",
  author: "James Clear"
};

// myBook.title = "Other"; ❌ lỗi – không thể gán lại

Khi dùng: Khi bạn muốn bảo vệ dữ liệu không bị thay đổi sau khi khởi tạo (tương tự như const cho object).


7. Exclude<T, U>

Tạo kiểu mới loại bỏ các kiểu trong U ra khỏi T.

Cú pháp:

Exclude<Type, ExcludedUnion>

Ví dụ:

type Primitive = string | number | boolean;
type WithoutBoolean = Exclude<Primitive, boolean>;
// Kết quả: string | number

Khi dùng: Khi bạn muốn loại bỏ một hoặc nhiều kiểu cụ thể khỏi union type.

8. Extract<T, U>

Ngược lại với Exclude — nó giữ lại các kiểu trong Ttrùng với U.

Cú pháp:

Extract<Type, Union>

Ví dụ:

type Mixed = string | number | boolean;
type OnlyStringOrNumber = Extract<Mixed, string | number>;
// Kết quả: string | number

Khi dùng: Khi bạn chỉ muốn giữ lại kiểu có trong tập hợp nào đó.

9. NonNullable<T>

Tạo kiểu mới từ T nhưng loại bỏ nullundefined.

Cú pháp:

NonNullable<Type>

Ví dụ:

type PossibleValues = string | number | null | undefined;
type CleanValues = NonNullable<PossibleValues>;
// Kết quả: string | number

Khi dùng: Khi bạn chắc chắn rằng dữ liệu sẽ không bao giờ là null hoặc undefined, và muốn TypeScript bắt lỗi nếu có trường hợp đó.

Tổng hợp nhanh

Utility Type Tác dụng Ví dụ ngắn
Pick<T, K> Chọn một số thuộc tính từ kiểu T `Pick<Person, "name"
Readonly<T> Biến toàn bộ thuộc tính thành chỉ đọc Readonly<Book>
Exclude<T, U> Loại bỏ các kiểu trong U khỏi T `Exclude<string
Extract<T, U> Giữ lại các kiểu trong T mà có trong U `Extract<string
NonNullable<T> Loại bỏ nullundefined khỏi T `NonNullable<string

Mẹo ghi nhớ nhanh

Mục tiêu Utility nên dùng
Chỉ lấy vài field trong object Pick
Loại bỏ vài field Omit
Cho phép bỏ qua field Partial
Bắt buộc tất cả field Required
Chống sửa đổi field Readonly
Loại bỏ kiểu không mong muốn Exclude
Giữ lại kiểu mong muốn Extract
Loại bỏ null/undefined NonNullable

Tóm lại

  • Utility types giúp giảm lập lại trong việc khai kiểu, đồng thời giữ được an toàn kiểu (type-safety).
  • Khi viết module, interface hoặc API response, hãy nghĩ tới việc “liệu có cần bản sao kiểu mà tất cả field optional / bỏ bớt field / kiểu map … không” → nếu có thì dùng Partial, Omit, Record, v.v.
  • Không nên dùng “một cách tùy tiện” mà không hiểu vì sao — vì sử dụng sai có thể làm kiểu dữ liệu mơ hồ, dẫn tới lỗi logic hoặc mất lợi ích kiểm tra kiểu.

All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí