Mục lục

Type Transformation: Mapped & Template Literal Types

Cơ chế tạo ra hàng nghìn kiểu dữ liệu từ design tokens một cách tự động và an toàn.

1. Vấn đề: Cạm bẫy "Copy-Paste" trong Design System

Hãy tưởng tượng bạn đang xây dựng một bộ UI Kit. Bạn có 20 màu sắc và mỗi màu có 5 sắc độ (light, dark, ...).

typescript:
type Color = "primary-light" | "primary" | "primary-dark" | "secondary-light" | ...;
// Bạn phải viết tay hàng trăm dòng như thế này

Thất bại:

  • Cực hình bảo trì: Mỗi khi Design team đổi tên một màu, bạn phải đi tìm và thay thế ở 10 file khác nhau.
  • Sai sót con người: Chỉ cần gõ nhầm một chữ cái (primari thay vì primary), hệ thống type sẽ sụp đổ.
  • Mất tính đồng nhất: Code của bạn và tài liệu Design ngày càng xa rời nhau.

Đây là lúc chúng ta cần đến sức mạnh "lập trình ra type" của Mapped TypesTemplate Literal Types.

2. Mapped Types (Kiểu ánh xạ)

Hãy tưởng tượng bạn có một interface Colors và bạn muốn tạo ra một interface mới mà tất cả các thuộc tính đều là tùy chọn (optional).

typescript:
type Colors = {
  primary: string;
  secondary: string;
  danger: string;
};

// Không cần viết lại, hãy dùng Mapped Type
type OptionalColors = {
  [K in keyof Colors]?: Colors[K];
};

2. Template Literal Types (Type chuỗi mẫu)

Được giới thiệu từ TS 4.1, đây là tính năng cho phép bạn biến các chuỗi thành type một cách linh hoạt như String Template trong JS.

typescript:
type Size = "sm" | "md" | "lg";
type Color = "primary" | "secondary";

// Tự động tạo ra: "sm-primary" | "sm-secondary" | "md-primary" | ...
type ButtonVariant = `${Size}-${Color}`;

3. Thử nghiệm: Tạo Design Tokens Type

Hãy chạy mã dưới đây để thấy cách TS tự động tạo ra một hệ thống type phức liệt từ những định nghĩa đơn giản nhất.

4. Kỹ thuật as trong Mapped Types (Key Remapping)

Bạn có thể thay đổi tên của key khi đang ánh xạ. Ví dụ: biến tất cả thuộc tính thành hàm getter.

typescript:
type Getters<T> = {
  [K in keyof T as `get${Capitalize<string & K>}`]: () => T[K];
};

interface User {
  name: string;
  age: number;
}

type UserGetters = Getters<User>;
// Kết quả: { getName: () => string; getAge: () => number; }

5. Tại sao Senior cần "nghiện" cái này?

  1. Single Source of Truth: Bạn chỉ cần định nghĩa danh sách màu sắc một lần trong file JS/JSON, TS sẽ tự động suy luận ra toàn bộ hệ thống type.
  2. Khả năng Bảo trì: Khi thêm một màu mới vào hệ thống, bạn không cần phải đi update hàng chục file type khác nhau.
  3. DX (Developer Experience): Cung cấp Auto-complete cực kỳ chính xác cho các developer khác khi sử dụng thư viện UI của bạn.

Tình huống thực tế & Phỏng vấn

Scenario: Đồng bộ hóa Design Tokens và Code

Bối cảnh: Team Design vừa cập nhật bộ màu sắc trong Figma, thêm các biến màu như success-light, success-dark. Bạn cần cập nhật code để developer có thể dùng các biến này trong Button component.

Vấn đề: Nếu bạn cập nhật thủ công các Union Types, bạn sẽ bỏ sót nhiều chỗ và dễ gõ sai chính tả.

Giải pháp Senior: Sử dụng Template Literal Types để tự động tạo ra bộ variants.

typescript:
type Status = "success" | "warning" | "error";
type Intensity = "light" | "default" | "dark";

type NewColors = `${Status}${Intensity extends "default" ? "" : `-${Intensity}`}`;
// Kết quả: "success" | "success-light" | "success-dark" | ...

Interview Question: Senior Level

Q: Mapped Types có thể được dùng để tạo ra một "Deep Readonly" type không? Hãy giải thích cơ chế đằng sau việc ép mọi thuộc tính (kể cả object con) thành readonly.

Gợi ý đáp án

Đáp án: Có, chúng ta có thể dùng đệ quy (recursion) trong Mapped Types.

typescript:
type DeepReadonly<T> = {
  readonly [K in keyof T]: T[K] extends object ? DeepReadonly<T[K]> : T[K];
};

Cơ chế: TS duyệt qua từng key K, nếu giá trị là một object, nó sẽ gọi lại chính nó để khóa tiếp các thuộc tính bên trong. Nếu không phải object, nó chỉ thêm modifier readonly.


Tổng kết

Mapped và Template Literal Types biến TypeScript từ một hệ thống kiểm tra lỗi đơn thuần trở thành một Programming Language for Types. Ở cấp độ Senior, bạn không "dùng" type, bạn "lập trình" ra type.

Quảng cáo
mdhorizontal