Kiểu mảng (array) trong TypeScript là kiểu phần tử theo sau bởi []: string[] là mảng chuỗi, number[] là mảng số. Cách viết generic Array<string> là cùng một kiểu. Khi mảng đã có kiểu, mọi phần tử bạn thêm vào và mọi phần tử bạn đọc ra đều mang kiểu đó.
Dòng @ts-expect-error là lỗi biên dịch (TS2345). Ở đây nó được đánh dấu là lỗi mong đợi để khối code vẫn chạy được, và vì kiểu bị xóa khi chạy, 42 thật sự được thêm vào lúc runtime: đầu ra cho thấy điều đó.
string[] và Array<string>
| Cách viết | Tương đương | Ghi chú |
|---|---|---|
string[] | Array<string> | Cách viết phổ biến. |
(string | number)[] | Array<string | number> | Bắt buộc có ngoặc: string | number[] nghĩa là "một chuỗi, hoặc một mảng số". |
readonly string[] | ReadonlyArray<string> | Không có push, pop, sort hay gán theo chỉ số. |
User[] | Array<User> | Mảng đối tượng dùng kiểu của đối tượng. |
string[][] | Array<Array<string>> | Mảng 2 chiều (lưới). |
Hãy chọn một phong cách cho cả codebase. Quy tắc array-type của typescript-eslint mặc định dùng T[].
Mảng đối tượng
Mô tả phần tử bằng một type alias hoặc interface, rồi dùng Type[]. Mọi thứ đọc ra từ mảng đều được kiểm tra theo cấu trúc đó.
Một object literal thêm vào users phải khớp chính xác với User: thiếu admin hoặc viết sai tên thuộc tính đều là lỗi biên dịch.
map, filter, reduce và find có kiểu
Các phương thức của mảng là generic, nên kết quả của chúng mang kiểu. Mỗi phương thức làm gì lúc runtime đã có ở trang phương thức mảng của JavaScript; phần kiểu là thứ TypeScript bổ sung:
| Phương thức | Kiểu kết quả trên T[] |
|---|---|
map(fn) | U[], trong đó U là kiểu fn trả về |
filter(fn) | T[] (hoặc một kiểu hẹp hơn, xem bên dưới) |
find(fn) | T | undefined |
findIndex(fn), indexOf(x) | number (-1 nếu không có) |
some(fn), every(fn), includes(x) | boolean |
reduce(fn, init) | kiểu của init (hoặc đối số kiểu, reduce<R>(...)) |
at(i) | T | undefined |
join(sep) | string |
Ví dụ cuối chạy được vì TypeScript (từ bản 5.5) suy luận rằng (n) => n !== undefined là một type predicate, nên filter trả về number[] thay vì (number | undefined)[]. Với những phép kiểm tra nó không suy luận được, hãy tự viết predicate: filter((x): x is User => x !== null).
Mảng chứa nhiều hơn một kiểu
Kiểu phần tử là union cho phép trộn lẫn. Union của các kiểu mảng thì không:
Khi mỗi vị trí có kiểu cố định, như cặp [name, age], hãy dùng tuple: [string, number] biết chỉ số 0 là chuỗi và chỉ số 1 là số, còn (string | number)[] thì không.
Mảng readonly
readonly T[] loại bỏ mọi phương thức làm thay đổi mảng. Dùng nó cho tham số mà hàm không được thay đổi, và cho hằng số.
index.ts(3,12): error TS2339: Property 'push' does not exist on type 'readonly number[]'.
Xóa dòng push đi và khối code in ra 4. Một number[] có thể thay đổi luôn truyền được vào chỗ cần readonly number[], nên tham số readonly không gây bất tiện gì cho nơi gọi. Phép kiểm tra chỉ có lúc biên dịch: lúc runtime nó là một mảng bình thường. Để sắp xếp một mảng readonly, hãy sắp xếp bản sao: [...values].sort().
Cái bẫy includes với mảng literal
as const biến một mảng thành tuple readonly gồm các kiểu literal. Điều này hữu ích cho danh sách giá trị cho phép, nhưng khi đó includes của nó chỉ nhận đúng các literal ấy:
Bọc phép kiểm tra trong một type guard (value is Color) nghĩa là việc mở rộng kiểu chỉ xảy ra một lần, và nơi gọi nhận lại một giá trị đã được thu hẹp.
Truy cập theo chỉ số và mảng rỗng
Đọc arr[i] cho kiểu T, kể cả khi i nằm ngoài phạm vi và giá trị lúc runtime là undefined. at(i) có kiểu T | undefined, và tùy chọn trình biên dịch noUncheckedIndexedAccess cũng khiến việc truy cập chỉ số thông thường trả về T | undefined.
queue[0].toUpperCase() vẫn biên dịch được nhưng sẽ ném TypeError lúc runtime. Hãy ưu tiên at(), kiểm tra độ dài, hoặc noUncheckedIndexedAccess khi một chỉ số có thể không tồn tại.
Câu hỏi thường gặp
Khai báo kiểu mảng trong TypeScript như thế nào?
Viết kiểu phần tử rồi thêm []: let names: string[] = ["a", "b"]. Dạng generic Array<string> có nghĩa y hệt. Với mảng đối tượng, dùng một object type hoặc interface làm kiểu phần tử: User[].
string[] và Array<string> khác nhau thế nào?
Không khác gì: đó là hai cách viết của cùng một kiểu. string[] phổ biến hơn. Dạng generic dễ đọc hơn với kiểu phần tử phức tạp, và với mảng readonly, readonly string[] và ReadonlyArray<string> cũng là một.
Vì sao find trả về undefined trong TypeScript?
array.find() trả về T | undefined vì có thể không có phần tử nào khớp. Với strict, bạn phải xử lý trường hợp undefined bằng câu lệnh if, optional chaining (found?.name) hoặc giá trị mặc định (found ?? fallback) trước khi dùng kết quả.
Khai báo kiểu mảng chứa nhiều kiểu trong TypeScript thế nào?
Dùng kiểu phần tử là union đặt trong ngoặc: (string | number)[] là mảng mà mỗi phần tử là chuỗi hoặc số. Kiểu này khác với string[] | number[], nghĩa là hoặc mảng chỉ có chuỗi hoặc mảng chỉ có số. Khi thứ tự các kiểu cố định, như [string, number], hãy dùng tuple.
Vì sao includes báo lỗi trên mảng as const?
Một mảng readonly gồm các literal, như ["red", "green"] as const, có includes(searchElement: "red" | "green"), nên truyền vào một string thông thường sẽ gây lỗi TS2345. Hãy mở rộng kiểu mảng khi kiểm tra, (COLORS as readonly string[]).includes(input), tốt nhất là đặt trong một type guard thu hẹp input về union các literal.