Cách sửa lỗi TypeScript 'Source has X element(s) but target requires Y'

begin🔵 TypeScript2026-07-28| TypeScript 3.0+, Node.js, mọi hệ điều hành (Windows, macOS, Linux), VS Code.

Error Message

Type '[string, number]' is not assignable to type '[string, number, boolean]'. Source has 2 element(s) but target requires 3.
#typescript#tuple#mảng#phát-triển-web

Vấn đềTypeScript coi tuple (bộ dữ liệu) như những bản thiết kế nghiêm ngặt. Khác với mảng (array) thông thường có thể tăng hoặc giảm kích thước, tuple là một tập hợp có kích thước cố định, trong đó mỗi vị trí đều có mục đích cụ thể. Khi gặp lỗi này, điều đó có nghĩa là bạn đang cố gắng đưa một mẩu dữ liệu nhỏ vào một vị trí được thiết kế cho thứ gì đó lớn hơn.

Hãy tưởng tượng bạn đang định nghĩa một hệ tọa độ cho một engine 3D:

type Point3D = [number, number, number];

// Lỗi: Nguồn có 2 phần tử nhưng đích yêu cầu 3
const position: Point3D = [10, 20];

Trình biên dịch mong đợi ba số. Vì bạn chỉ cung cấp hai, nó sẽ báo lỗi vì phần tử thứ ba về mặt kỹ thuật sẽ là undefined, không khớp với kiểu number mà bản thiết kế của bạn yêu cầu.

Nguyên nhân gốc rễTheo mặc định, các tuple trong TypeScript có thuộc tính length được quy định cứng. Nếu bạn định nghĩa một tuple là [string, string], độ dài của nó chính xác là 2. Việc gán một mảng có độ dài 1 hoặc 3 sẽ vi phạm quy tắc đó. Trình biên dịch không chỉ kiểm tra kiểu dữ liệu; nó còn thực thi cấu trúc của chính vật chứa đó.

Giải pháp 1: Sử dụng các phần tử Tuple tùy chọnBạn có thực sự cần giá trị thứ ba đó mọi lúc không? Nếu một số phần tử chỉ thỉnh thoảng mới cần thiết, hãy đánh dấu chúng là tùy chọn bằng ký hiệu ?. Đây là cách sạch nhất để xử lý các độ dài dữ liệu khác nhau mà không làm mất đi tính an toàn của kiểu dữ liệu (type safety).

// Phần tử thứ ba hiện là tùy chọn
type UserResponse = [string, number, boolean?];

const userA: UserResponse = ["Alice", 200, true]; // Hoạt động tốt
const userB: UserResponse = ["Bob", 404];         // Cũng hoạt động tốt

Lưu ý rằng các phần tử tùy chọn phải nằm ở cuối tuple. Bạn không thể đặt một phần tử bắt buộc sau một phần tử tùy chọn, vì điều đó sẽ khiến việc lập chỉ mục (indexing) trở nên mơ hồ đối với trình biên dịch.

Giải pháp 2: Sử dụng Rest Elements cho độ dài linh hoạtĐôi khi bạn biết danh sách bắt đầu như thế nào, nhưng không biết nó kết thúc ra sao. Trong những trường hợp này, hãy sử dụng toán tử rest (...) để cho phép bất kỳ số lượng phần tử bổ sung nào. Điều này chuyển đổi phần cuối của tuple thành một mảng mở rộng một cách hiệu quả.

// Yêu cầu mã trạng thái và thông điệp, sau đó là số lượng tag bất kỳ
type LogEntry = [number, string, ...string[]];

const simpleLog: LogEntry = [200, "OK"];
const detailedLog: LogEntry = [500, "Error", "server", "database", "retry_failed"];

Điều này đặc biệt hữu ích cho các đối số của hàm hoặc khi xử lý dữ liệu dạng CSV, nơi một vài cột đầu tiên là cố định nhưng các cột còn lại là biến đổi.

Giải pháp 3: Khẳng định kiểu - Type Assertion (Cách khắc phục nhanh)Nếu bạn đang lấy dữ liệu từ một API bên ngoài và bạn chắc chắn 100% rằng cấu trúc là chính xác bất kể trình biên dịch nghĩ gì, bạn có thể sử dụng khẳng định kiểu (type assertion). Hãy coi đây là giải pháp cuối cùng. Nó bảo TypeScript hãy "tin tưởng bạn", điều này sẽ bỏ qua hoàn toàn việc kiểm tra an toàn.

type Coordinates = [number, number, number];
const rawData = [1.5, 2.5] as unknown as Coordinates;

Hãy sử dụng cách này một cách tiết chế. Nếu dữ liệu thực sự bị thiếu lúc runtime (thời gian chạy), mã của bạn có thể bị sập khi cố gắng truy cập rawData[2], và TypeScript sẽ không cảnh báo bạn về điều đó.

Giải pháp 4: Cập nhật chữ ký hàm (Function Signatures)Bạn có thể gặp phải lỗi này khi truyền dữ liệu vào một hàm. Nếu hàm mong đợi một tuple có ba mục nhưng dữ liệu của bạn là linh hoạt, bạn nên cập nhật chữ ký hàm để xử lý các giá trị tùy chọn. Phân rã (destructuring) với các giá trị mặc định là một cách tuyệt vời để xử lý việc này một cách an toàn.

function setConfig(config: [string, number, boolean?]) {
    const [theme, version, isEnabled = true] = config;
    console.log(`Đang thiết lập ${theme} phiên bản v${version}. Kích hoạt: ${isEnabled}`);
}

Xác minhSau khi áp dụng bản sửa lỗi, hãy xác minh công việc của bạn bằng ba bước sau:

  • Kiểm tra trình soạn thảo: Đảm bảo các đường gạch chân ngoằn ngoèo màu đỏ trong VS Code đã biến mất.- Chạy trình biên dịch: Thực thi lệnh npx tsc. Việc sửa lỗi thành công sẽ dẫn đến kết thúc sạch sẽ mà không có thông báo lỗi nào.- Kiểm tra logic Runtime: Nếu bạn đã sử dụng các phần tử tùy chọn, hãy đảm bảo logic của bạn không bị hỏng khi phần tử đó là undefined.## Các phương pháp hay nhất để phòng ngừaTránh những rắc rối này trong tương lai bằng cách tuân theo các mẫu sau:
  • Ưu tiên sử dụng Object: Nếu tuple của bạn có nhiều hơn ba phần tử, thường thì tốt hơn là sử dụng một interface. Các đối tượng (object) dễ đọc hơn và không phụ thuộc vào thứ tự nghiêm ngặt.- Sử dụng Readonly: Sử dụng readonly [number, number] để ngăn chặn các lệnh gọi .push() hoặc .pop() vô tình làm thay đổi độ dài tuple trong quá trình thực thi.- Khai báo kiểu trả về rõ ràng: Khi tạo các React hook tùy chỉnh trả về mảng, hãy luôn khai báo kiểu trả về một cách rõ ràng dưới dạng tuple. Nếu không, TypeScript có thể suy luận nó là một mảng chuẩn, dẫn đến lỗi độ dài về sau.

Related Error Notes