Chuyển đến nội dung chính
Mô-đun 2 · Bài học 2 trong tổng số 24/14 trong khóa học~15 min
Bài học theo mô-đun (2/2)

Công đoàn và thu hẹp

Trong thế giới thực, các biến và phản hồi API không phải lúc nào cũng có một loại cố định. TypeScript cung cấp Các loại kết hợp để xử lý tính biến đổi và Thu hẹp loại để vận hành an toàn trên chúng trong thời gian chạy.


Các loại liên minh

Kiểu kết hợp cho phép một biến chấp nhận các giá trị thuộc các loại khác nhau. Nó được thể hiện bằng ký hiệu thanh dọc (|):

TS
let result: number | string;
result = 42; // Valid
result = 'Error 404'; // Valid

Tuy nhiên, khi làm việc với loại kết hợp, chúng ta không thể gọi trực tiếp các phương thức chỉ thuộc về một trong các loại đó (ví dụ: chúng ta không thể gọi .toUpperCase() nếu biến cũng có thể là number). Đầu tiên chúng ta phải “thu hẹp” loại hình.


Thu hẹp loại

Thu hẹp loại là quá trình trong đó TypeScript phân tích cấu trúc luồng điều khiển (chẳng hạn như if hoặc switch) để suy ra loại cụ thể hơn cho một biến trong thời gian chạy.

Có một số cách để thực hiện thu hẹp:

1. Toán tử typeof

Lý tưởng để phân biệt các loại nguyên thủy:

TS
function printLength(value: string | number) {
  if (typeof value === 'string') {
    // TypeScript knows 'value' is a string here
    console.log(value.length);
  } else {
    // TypeScript knows 'value' is a number here
    console.log(value.toFixed(2));
  }
}

2. Toán tử in

Được sử dụng để kiểm tra xem một thuộc tính cụ thể có tồn tại trên một đối tượng hay không:

TS
interface Fish {
  swim: () => void;
}
interface Bird {
  fly: () => void;
}

function move(animal: Fish | Bird) {
  if ('swim' in animal) {
    animal.swim(); // Narrowing to Fish
  } else {
    animal.fly(); // Narrowing to Bird
  }
}

Công đoàn bị phân biệt đối xử

Mẫu Discriminated Unions bao gồm việc tạo các đối tượng có chung thuộc tính với một giá trị theo nghĩa đen duy nhất (được gọi là discriminator). TypeScript nhận ra yếu tố phân biệt đối xử này và tự động thu hẹp loại trong các khối có điều kiện.

TS
interface SuccessResponse {
  status: 'success'; // Literal discriminator
  data: string;
}

interface ErrorResponse {
  status: 'error'; // Literal discriminator
  errorMessage: string;
}

type ApiResponse = SuccessResponse | ErrorResponse;

function handleResponse(response: ApiResponse) {
  if (response.status === 'success') {
    console.log('Data received:', response.data);
  } else {
    console.error('An error occurred:', response.errorMessage);
  }
}

Hãy tự mình thử

Bài tập 1: Các kiểu kết hợp

tập thể dục#ts.m2.l2.e1
Nỗ lực: 0Đang tải…

Khai báo một biến có tên id có thể là số hoặc chuỗi. Trước tiên hãy khởi tạo nó bằng số 101, sau đó gán giá trị chuỗi 'USER-101'.

Đang tải trình chỉnh sửa…
Hiển thị gợi ý

Sử dụng | toán tử để nối số và chuỗi trong khai báo biến let.

Giải pháp khả dụng sau 3 lần thử

Bài tập 2: Thu hẹp kiểu cơ bản

tập thể dục#ts.m2.l2.e2
Nỗ lực: 0Đang tải…

Tạo một hàm có tên formatInput chấp nhận đầu vào tham số thuộc loại chuỗi hoặc số. Nếu đầu vào là một chuỗi, trả về đầu vào được chuyển đổi thành chữ hoa. Nếu là số, trả về giá trị đầu vào nhân với 2. Chỉ định loại rõ ràng.

Đang tải trình chỉnh sửa…
Hiển thị gợi ý

Sử dụng typeof input === 'string' bên trong khối if để phân nhánh hành vi.

Giải pháp khả dụng sau 3 lần thử

Bài tập 3: Thu hẹp bằng “in”

tập thể dục#ts.m2.l2.e3
Nỗ lực: 0Đang tải…

Cho hai giao diện Ô tô (với phương thức drive) và Thuyền (với phương thức Sail), hãy viết một hàm có tên moveVehicle chấp nhận tham số phương tiện thuộc loại Ô tô hoặc Thuyền. Nếu phương tiện có thuộc tính drive, hãy thực thi phương thức drive(). Nếu không, hãy thực hiện phương thức Sail().

Đang tải trình chỉnh sửa…
Hiển thị gợi ý

Sử dụng toán tử in ở dạng 'drive' in Vehicle để thu hẹp giao diện.

Giải pháp khả dụng sau 3 lần thử

Bài tập 4: Hình dạng liên minh phân biệt đối xử

tập thể dục#ts.m2.l2.e4
Nỗ lực: 0Đang tải…

Xác định một loại Hình dạng là sự kết hợp của hai loại: Hình tròn và Hình vuông. Vòng tròn có thuộc tính loại được đặt thành 'vòng tròn' (giá trị bằng chữ) và bán kính (số). Hình vuông có thuộc tính loại được đặt thành 'vuông' (giá trị bằng chữ) và một cạnh (số). Sau đó viết hàm getArea nhận hình dạng kiểu Shape và trả về diện tích dưới dạng số (đối với hình tròn Math.PI * bán kính * bán kính, đối với cạnh hình vuông * cạnh).

Đang tải trình chỉnh sửa…
Hiển thị gợi ý

Sử dụng shape.kind === 'circle' bên trong getArea để phân biệt loại và tính diện tích chính xác.

Giải pháp khả dụng sau 3 lần thử