1
0
Posts
SC
Stus Codejosephminhlong

Xử lý Union Arrays với TypeScript Type Guards

Đăng vào 4 tháng trước

• 4 phút đọc

Chủ đề:

typescript

Trong quá trình phát triển frontend, việc xử lý các mảng phức tạp chứa các loại phần tử khác nhau là một nhiệm vụ phổ biến. TypeScript có thể cung cấp một lớp an toàn về kiểu dữ liệu khi làm việc với các mảng này. Tuy nhiên, việc phản ánh đúng điều này trong hệ thống kiểu có thể gặp khó khăn.

Ví Dụ

Chúng ta bắt đầu với việc định nghĩa một số kiểu đại diện cho các hình dạng khác nhau.

typescript Copy
// Hình vuông với thuộc tính kích thước của bốn cạnh.
interface Square {
  type: "SQUARE";
  size: number;
}

// Hình chữ nhật với thuộc tính chiều cao và chiều rộng.
interface Rectangle {
  type: "RECTANGLE";
  height: number;
  width: number;
}

// Hình tròn với thuộc tính bán kính.
interface Circle {
  type: "CIRCLE";
  radius: number;
}

// Kiểu hợp của tất cả các hình dạng có thể có.
type Shape = Square | Rectangle | Circle;

Bây giờ, chúng ta có thể sử dụng các kiểu này để định nghĩa một số hình dạng và thêm chúng vào một mảng.

language Copy
const circle1: Circle = { type: "CIRCLE", radius: 314 };
const circle2: Circle = { type: "CIRCLE", radius: 42 };
const square1: Square = { type: "SQUARE", size: 10 };
const square2: Square = { type: "SQUARE", size: 1 };
const rectangle1: Rectangle = { type: "RECTANGLE", height: 10, width: 4 };
const rectangle2: Rectangle = { type: "RECTANGLE", height: 3, width: 5 };

const shapes = [circle1, square1, rectangle1, square2, circle2, rectangle2];

Thách Thức

Bây giờ, hãy tìm hình vuông đầu tiên trong mảng này bằng phương thức find.

typescript Copy
const firstSquare = shapes.find((shape) => shape.type === "SQUARE");
console.log(firstSquare);

Điều này sẽ in ra hình vuông đầu tiên (square1) như mong đợi:

typescript Copy
{
  "type": "SQUARE",
  "size": 10
}

Tuy nhiên, chúng ta sẽ gặp rắc rối nếu cố truy cập thuộc tính size của hình vuông.

typescript Copy
const firstSquare = shapes.find((shape) => shape.type === "SQUARE");
console.log(firstSquare?.size);
//                       ^^^^
// Property 'size' does not exist on type 'Square | Rectangle | Circle'.
//  Property 'size' does not exist on type 'Rectangle'.(2339)

Cách Giải Quyết

Sử Dụng Ép Kiểu (Casts)

Giải pháp đơn giản nhất là ép kiểu kết quả của find.

typescript Copy
const firstCircle = shapes.find((shape) => shape.type === "SQUARE") as Square;
console.log(firstCircle?.size);

Kiểu Bảo Vệ (Type Guards)

TypeScript cung cấp nhiều cách để thu hẹp kiểu dữ liệu. Chúng ta có thể định nghĩa các kiểu bảo vệ cho từng hình dạng.

typescript Copy
const isSquare = (shape: Shape): shape is Square => shape.type === "SQUARE";
const isCircle = (shape: Shape): shape is Circle => shape.type === "CIRCLE";
const isRectangle = (shape: Shape): shape is Rectangle => shape.type === "RECTANGLE";

Sử dụng kiểu bảo vệ này với các câu lệnh if để truy cập an toàn các thuộc tính chỉ có trên một hình dạng cụ thể.

typescript Copy
const shape: Shape = square1;

if (isSquare(shape)) {
  console.log(shape.size);
}

Nâng Cao

Phương Thức Overload

Phương thức find có thể sử dụng một khai báo overload để thu hẹp kiểu dữ liệu.

typescript Copy
interface Array<T> {
  find<S extends T>(
    predicate: (value: T, index: number, obj: T[]) => value is S,
    thisArg?: any
  ): S | undefined;

  find(
    predicate: (value: T, index: number, obj: T[]) => unknown,
    thisArg?: any
  ): T | undefined;
}

Chúng ta có thể truyền trực tiếp kiểu bảo vệ vào phương thức find.

typescript Copy
const firstSquare = shapes.find(isSquare);
console.log(firstSquare?.size);

Gotcha

Kiểu bảo vệ cần được truyền trực tiếp vào phương thức find để hoạt động đúng cách.

typescript Copy
const firstCircle = shapes.find((shape) => isCircle(shape));
console.log(firstCircle?.radius);
//                       ^^^^^^
// Property 'radius' does not exist on type 'Square | Rectangle | Circle'.
Phương Thức filter
Phương thức filter cung cấp hai overload tương tự find.

interface Array<T> {
  filter<S extends T>(
    predicate: (value: T, index: number, array: T[]) => value is S,
    thisArg?: any
  ): S[];

  filter(
    predicate: (value: T, index: number, array: T[]) => unknown,
    thisArg?: any
  ): T[];
}

Ví dụ:

typescript Copy
const onlyCircles = shapes.filter(isCircle);
onlyCircles.forEach((circle) => console.log(circle.radius));

Kết Hợp

Nếu muốn tìm hình vuông với kích thước cụ thể, bạn có thể kết hợp filterfind.

typescript Copy
const sizeOneSquare = shapes.find(
  (shape) => isSquare(shape) && shape.size === 1
);
console.log(sizeOneSquare?.size);

Tổng Kết

Khi làm việc với các mảng chứa phần tử hỗn hợp, hãy xem xét việc sử dụng kiểu bảo vệ để cải thiện an toàn kiểu dữ liệu. Điều này không chỉ giúp mã nguồn rõ ràng hơn mà còn tránh được các lỗi tiềm ẩn.

Gợi ý câu hỏi phỏng vấn
Không có dữ liệu

Không có dữ liệu

Bài viết được đề xuất
Bài viết cùng tác giả

Bình luận

Chưa có bình luận nào

Chưa có bình luận nào