Giới thiệu
hocvietcode.com là website chia sẻ và cập nhật tin tức công nghệ, chia sẻ kiến thức, kỹ năng. Chúng tôi rất cảm ơn và mong muốn nhận được nhiều phản hồi để có thể phục vụ quý bạn đọc tốt hơn !
Liên hệ quảng cáo: [email protected]
Giả sử bạn cần lấy phần tử đầu tiên của mảng tên khóa học, rồi lại cần làm điều tương tự với mảng số. Viết hai hàm giống nhau gây lặp; dùng any làm mất thông tin kiểu. Generics mô tả mối liên hệ giữa đầu vào và đầu ra để một định nghĩa có thể làm việc với nhiều kiểu cụ thể.
Mục lục
function first<T>(items: readonly T[]): T | undefined {
return items[0];
}
const firstTitle = first(["HTML", "TypeScript"]);
const firstId = first([10, 20]);
console.log(firstTitle?.toUpperCase()); // HTML
console.log(firstId); // 10
T là tham số kiểu, không phải một biến nhận giá trị lúc chạy. Trong lần gọi đầu, TypeScript suy luận T là string; lần tiếp theo T là number. Kiểu trả về có undefined vì mảng có thể rỗng. Với noUncheckedIndexedAccess, trình biên dịch cũng giúp nhắc trường hợp này.
Readonly ở tham số cho biết hàm chỉ đọc mảng thông qua tham chiếu này. Không nhất thiết phải truyền kiểu tường minh; khi cần mô tả một mảng rỗng có chủ ý, bạn có thể viết first<string>([]). Kết quả lúc chạy vẫn là undefined, vì khai báo kiểu không tạo phần tử cho mảng.
interface Page<T> {
items: T[];
total: number;
}
interface Course {
id: number;
title: string;
}
const page: Page<Course> = {
items: [{ id: 1, title: "TypeScript" }],
total: 1
};
Page mô tả cấu trúc phân trang chung, còn T quyết định kiểu của từng phần tử. Page<Course> và Page<string> chia sẻ cách tổ chức nhưng giữ thông tin khác nhau ở items. Định nghĩa này chỉ kiểm tra cách dùng trong mã; nó không tự xác nhận kết quả trả về từ một API.
function printTitle<T extends { title: string }>(item: T): T {
console.log(item.title);
return item;
}
const course = printTitle({
id: 1,
title: "TypeScript",
free: true
});
console.log(course.free); // true
Nếu T không có ràng buộc, hàm không được giả định item có title. Extends ở đây yêu cầu T có ít nhất thuộc tính title kiểu string; nó không mang nghĩa kế thừa class tại thời điểm chạy. Trả lại T giúp giữ cả id và free trong kiểu kết quả, thay vì làm chúng biến mất khỏi thông tin mà người gọi nhìn thấy.
function getProperty<T, K extends keyof T>(item: T, key: K): T[K] {
return item[key];
}
const title = getProperty(course, "title"); // string
const free = getProperty(course, "free"); // boolean
// getProperty(course, "price"); // Bỏ chú thích sẽ báo lỗi
Keyof T tạo một union các khóa của T. Ràng buộc K extends keyof T giới hạn key vào những khóa hợp lệ. T[K] là kiểu của thuộc tính tương ứng, nên lời gọi lấy title có kết quả string thay vì union của mọi kiểu thuộc tính. Đây là ví dụ nhỏ về kiểu đầu ra phụ thuộc vào đầu vào.
Dùng khi có một quan hệ kiểu cần được giữ: phần tử và mảng, khóa và object, hoặc dữ liệu và cấu trúc bao quanh. Nếu một hàm chỉ xử lý title và không cần trả lại thông tin của đầu vào, tham số { title: string } có thể đủ. Không cần thêm T vào mọi hàm để làm mã trông nâng cao.
Sau bài này, bạn có thể đọc lại Partial, Pick, Omit và Record trong phần kiểu dữ liệu 3: các ký hiệu trong dấu < > là tham số kiểu. Bước đầu chỉ cần dùng đúng chúng; conditional type và mapped type chuyên sâu có thể học sau. Để chạy các ví dụ cùng nhau, giữ thứ tự định nghĩa và thêm export {} ở cuối src/main.ts.
Đọc thêm: TypeScript: Generics.
Đọc thêm: TypeScript: keyof.
Bài trước: TypeScript cơ bản – Null, undefined, narrowing và type guards
Bài tiếp theo: TypeScript cơ bản – Module, import, export và tổ chức nhiều file
hocvietcode.com là website chia sẻ và cập nhật tin tức công nghệ, chia sẻ kiến thức, kỹ năng. Chúng tôi rất cảm ơn và mong muốn nhận được nhiều phản hồi để có thể phục vụ quý bạn đọc tốt hơn !
Liên hệ quảng cáo: [email protected]