Union, Intersection và Literal Types

Bài 4 – Union Type, Intersection Type và Literal Type trong TypeScript theo cách đơn giản, trực quan và tiêu chuẩn.

4/1/2024 DaiPhan
Bài 4 / 17

Union, Intersection và Literal Types

Union, Intersection và Literal types là những công cụ mạnh mẽ giúp bạn kết hợp và kiểm soát kiểu dữ liệu một cách linh hoạt. Chúng giúp code vừa chặt chẽ vừa linh hoạt, đáp ứng các trường hợp sử dụng phức tạp.


1. Nội dung chính

  • Union Type (|): Biến có thể mang một trong nhiều kiểu
  • Intersection Type (&): Kết hợp nhiều kiểu thành một
  • Literal Types: Giá trị cố định, không chỉ kiểu
  • Template Literal Types: Tạo kiểu từ string templates
  • Ứng dụng thực tế: Configuration, API responses, state management

2. Ví dụ chi tiết

2.1. Union Type

// Union cơ bản
let value: string | number;
value = "hello";
value = 42;

// Function với union parameters
function processId(id: string | number) {
  if (typeof id === "string") {
    return id.toUpperCase(); // string
  }
  return id.toFixed(2); // number
}

// Union với arrays
const mixed: (string | number)[] = [1, "two", 3, "four"];

// Discriminated unions - pattern quan trọng
type LoadingState = {
  status: "loading";
};

type SuccessState = {
  status: "success";
  data: any;
};

type ErrorState = {
  status: "error";
  error: string;
};

type AppState = LoadingState | SuccessState | ErrorState;

function renderUI(state: AppState) {
  switch (state.status) {
    case "loading":
      return "Loading...";
    case "success":
      return `Data: ${state.data}`;
    case "error":
      return `Error: ${state.error}`;
  }
}

2.2. Intersection Type

// Intersection cơ bản
type Person = {
  name: string;
  age: number;
};

type Employee = {
  id: number;
  department: string;
};

type EmployeePerson = Person & Employee;

const emp: EmployeePerson = {
  name: "Alice",
  age: 30,
  id: 123,
  department: "Engineering"
};

// Intersection với utility types
type RequiredUser = Required<Person> & {
  id: number;
};

// Intersection trong thực tế
type BaseEntity = {
  id: string;
  createdAt: Date;
  updatedAt: Date;
};

type SoftDeletable = {
  deletedAt?: Date;
  isDeleted: boolean;
};

type User = BaseEntity & {
  name: string;
  email: string;
} & SoftDeletable;

const user: User = {
  id: "123",
  createdAt: new Date(),
  updatedAt: new Date(),
  name: "Bob",
  email: "bob@example.com",
  isDeleted: false
};

2.3. Literal Types

// String literals
type Theme = "light" | "dark" | "system";
type Status = "pending" | "approved" | "rejected";

function setTheme(theme: Theme) {
  // theme chỉ có thể là "light", "dark", hoặc "system"
  document.body.setAttribute("data-theme", theme);
}

// Number literals
type Priority = 1 | 2 | 3 | 4 | 5;
type HttpStatus = 200 | 201 | 400 | 401 | 404 | 500;

function checkPriority(priority: Priority) {
  if (priority <= 2) {
    return "High priority";
  }
  return "Normal priority";
}

// Boolean literals
type Config = {
  debug: true | false;
  experimental: boolean;
};

// Mixed literals
type HttpMethod = "GET" | "POST" | "PUT" | "DELETE";
type ApiResponse = {
  method: HttpMethod;
  status: HttpStatus;
  success: true | false;
};

2.4. Template Literal Types

// Template literals cơ bản
type EventName = `on${Capitalize<string>}`;
type ClickEvent = `onClick${string}`;

// CSS properties
type CssProperty = `margin-${"top" | "bottom" | "left" | "right"}`;
type Padding = `padding-${"top" | "bottom" | "left" | "right"}`;

// API endpoints
type ApiEndpoint = `/api/${"users" | "products" | "orders"}/${string}`;

function makeRequest(endpoint: ApiEndpoint) {
  // endpoint có thể là "/api/users/123", "/api/products/456", etc.
}

// Advanced template literals
type Size = "small" | "medium" | "large";
type Color = "red" | "blue" | "green";
type ButtonVariant = `${Size}-${Color}` | "outline";

const button1: ButtonVariant = "small-red";
const button2: ButtonVariant = "outline";
// const button3: ButtonVariant = "extra-large-yellow"; // ❌ Error

3. Kiến thức trọng tâm

3.1. Union Type giúp xử lý nhiều trường hợp

  • Flexibility với control: Biến có thể nhận nhiều kiểu khác nhau
  • Type narrowing với typeof, instanceof, type guards
  • Discriminated unions cho state management và error handling

💡 GHI NHỚ: Union type = “CÓ THỂ là A HOẶC B”

3.2. Intersection Type giúp kết hợp features

  • Composition over inheritance: Kết hợp nhiều type thành một
  • Mixin pattern: Thêm features vào existing types
  • Plugin architecture: Kết hợp các plugin types

💡 GHI NHỚ: Intersection type = “PHẢI CẢ A VÀ B”

3.3. Literal Types giúp constraint values

  • Const assertions: as const để infer literal types
  • String enums alternative: Khi không muốn dùng enum
  • Configuration: Chỉ cho phép specific values

4. Bài tập thực hành

Bài 1: Union cho User roles

type UserRole = "admin" | "editor" | "viewer" | "guest";

type User = {
  id: number;
  name: string;
  role: UserRole;
  permissions?: string[];
};

function canEdit(user: User): boolean {
  return user.role === "admin" || user.role === "editor";
}

const admin: User = { id: 1, name: "Alice", role: "admin" };
const guest: User = { id: 2, name: "Bob", role: "guest" };

console.log(canEdit(admin)); // true
console.log(canEdit(guest)); // false

Bài 2: Intersection cho geometry

type Shape2D = {
  area: () => number;
};

type Drawable = {
  draw: () => void;
};

type Circle = Shape2D & Drawable & {
  radius: number;
  name: "circle";
};

const circle: Circle = {
  radius: 5,
  name: "circle",
  area: () => Math.PI * 5 * 5,
  draw: () => console.log("Drawing circle")
};

Bài 3: Template literals cho CSS

type ResponsiveValue<T> = {
  mobile: T;
  tablet?: T;
  desktop: T;
};

type SpacingScale = 0 | 4 | 8 | 16 | 24 | 32;
type Margin = `m-${SpacingScale}`;
type Padding = `p-${SpacingScale}`;

type ComponentStyles = {
  [K in Margin | Padding]?: ResponsiveValue<string>;
};

const styles: ComponentStyles = {
  "m-16": {
    mobile: "16px",
    desktop: "24px"
  },
  "p-8": {
    mobile: "8px",
    tablet: "12px",
    desktop: "16px"
  }
};

5. Sai lầm thường gặp

  • Quên type guard: Không kiểm tra type trước khi dùng union
  • Union thay vì intersection: Nhầm lẫn “hoặc” và “và”
  • Literal type quá rộng: Không bó hẹp giá trị đủ
  • Không sử dụng discriminated unions: Code become verbose
  • Template literals phức tạp: Over-engineering simple cases

⚠️ GHI NHỚ: Chọn đúng công cụ cho đúng công việc


6. Kết luận

Union, Intersection và Literal types giúp bạn xây dựng type system linh hoạt và mạnh mẽ. Khi kết hợp đúng cách, chúng giúp code vừa an toàn vừa expressive.

🔑 GHI NHỚ QUAN TRỌNG:

  • Union (|): Multiple possibilities
  • Intersection (&): Combine requirements
  • Literal: Specific values
  • Template literals: Dynamic type creation
  • Discriminated unions: Best practice for state management
17 bài học
Bài 4
Tiến độ hoàn thành 24%

Đã hoàn thành 4/17 bài học