Menu

Pick trong TypeScript: chọn thuộc tính từ một kiểu (ví dụ)

Pick<T, K> tạo một kiểu chỉ gồm các thuộc tính của T có khóa nằm trong K. Tìm hiểu Pick với một hoặc nhiều khóa, cách nó kiểm tra khóa, Pick so với Omit, chọn từ kiểu lồng nhau, hàm pick() có kiểu và chọn thuộc tính theo kiểu giá trị.

Trang này có trình soạn thảo chạy trực tiếp. Sửa, chạy và xem đầu ra ngay lập tức.

Pick<T, K> là một utility type có sẵn, tạo ra kiểu object chỉ gồm các thuộc tính của T được nêu trong K. Dùng nó khi một hàm hay component chỉ cần vài trường của một kiểu lớn hơn:

renderPreview nhận cả một User đầy đủ lẫn một object { id, name } đơn giản, vì nó chỉ yêu cầu đúng những gì nó dùng. Điều đó giúp hàm dễ gọi và dễ test hơn. Pick chỉ tác động lên kiểu: object ada đầy đủ vẫn có cả năm thuộc tính lúc chạy.

Cú pháp và kiểm tra khóa

Pick<Type, Keys>

Keys là một khóa hoặc một union các khóa, và mỗi khóa đều phải tồn tại trên Type. Định nghĩa trong thư viện chuẩn của TypeScript biến điều đó thành một ràng buộc:

type Pick<T, K extends keyof T> = {
  [P in K]: T[P];
};

Nhờ K extends keyof T, một khóa viết sai chính tả hoặc không tồn tại sẽ là lỗi biên dịch, và trình soạn thảo có thể tự động gợi ý tên khóa:

Lỗi là error TS2344: Type '"cost"' does not satisfy the constraint 'keyof Product'. Đây là điểm khác với Omit, vốn không kiểm tra khóa.

Pick giữ readonly và tùy chọn

Pick ánh xạ qua các khóa của T, nên mỗi thuộc tính được chọn giữ nguyên modifier của nó:

Dòng cuối in ra { id: 8 } vì readonly chỉ là kiểm tra lúc biên dịch: phép gán đã bị báo lỗi, bị chặn thông báo bằng @ts-expect-error, và vẫn chạy. Để đổi modifier trong lúc chọn, hãy bọc kết quả: Partial<Pick<Account, "plan">> hoặc Required<Pick<Account, "email">>.

So sánh Pick và Omit

Pick nêu những gì cần giữ; Omit nêu những gì cần bỏ. Hiện tại cả hai cho cùng kết quả, nhưng chúng hoạt động khác nhau khi kiểu nguồn có thêm thuộc tính:

Pick<T, K>Omit<T, K>
Bạn liệt kêcác khóa cần giữcác khóa cần bỏ
Thuộc tính mới trên Tkhông được đưa vàotự động được đưa vào
Khóa không tồn tại trong Klỗi biên dịch (TS2344)được chấp nhận mà không báo gì
Phù hợp vớiresponse của API, dữ liệu công khai, props"mọi thứ trừ phần bí mật"

Với dữ liệu rời khỏi máy chủ của bạn, Pick là lựa chọn mặc định an toàn hơn: không có gì xuất hiện trong kiểu trừ khi ai đó cố ý thêm vào.

Chọn từ kiểu lồng nhau

Pick chỉ thấy các khóa ở cấp ngoài cùng: một đường dẫn có dấu chấm như Pick<Customer, "address.city"> là lỗi (TS2344), không phải phép chọn lồng nhau. Hãy dùng indexed access type để đi tới kiểu lồng bên trong, rồi chọn từ đó:

Customer["address"] là kiểu của thuộc tính address. Cú pháp này cũng đi tới được phần tử mảng: với thuộc tính orders: Order[], Customer["orders"][number] là Order.

Hàm pick() có kiểu

Pick không làm gì lúc chạy. Một hàm generic nhỏ sao chép các thuộc tính được chọn sang object mới và trả về đúng Pick<T, K>:

K extends keyof T cho phép TypeScript suy ra K từ các đối số chuỗi, nên kiểu trả về có đúng những khóa được chọn. Phép ép kiểu {} as Pick<T, K> là cần thiết vì object rỗng chưa có các thuộc tính đó; vòng lặp sẽ điền chúng vào.

Chọn thuộc tính theo kiểu giá trị

Pick chọn theo tên khóa. Để chọn theo kiểu của thuộc tính, ví dụ "mọi trường kiểu chuỗi", hãy viết một mapped type có bộ lọc khóa:

Mệnh đề as ánh xạ mỗi khóa thành chính nó khi kiểu khớp, và thành never trong trường hợp ngược lại, rồi các khóa never bị loại bỏ. Một điểm tinh tế: kiểu của thuộc tính tùy chọn có chứa undefined, nên subtitle?: string sẽ không được tính là string ở đây; hãy so với string | undefined để bao gồm nó.

Câu hỏi thường gặp

Pick trong TypeScript dùng để làm gì?

Pick<T, K> tạo một kiểu object chỉ chứa các thuộc tính của T có khóa nằm trong K. Pick<User, "id" | "name"> là { id: number; name: string } khi User có hai thuộc tính đó cùng những thuộc tính khác. Nó chỉ tác động lên kiểu và không xóa gì khỏi object lúc chạy.

Pick và Omit khác nhau thế nào?

Pick liệt kê các thuộc tính cần giữ, Omit liệt kê các thuộc tính cần bỏ. Khi kiểu nguồn có thêm thuộc tính, Omit tự động đưa nó vào còn Pick thì không. Pick cũng kiểm tra các khóa có tồn tại trên kiểu hay không; Omit nhận bất kỳ khóa nào.

Làm sao chọn nhiều thuộc tính cùng lúc trong TypeScript?

Truyền một union các khóa: Pick<User, "id" | "name" | "email">. Mọi khóa đều phải tồn tại trên User, nếu không trình biên dịch sẽ báo lỗi TS2344.

Làm sao chọn một thuộc tính lồng nhau trong TypeScript?

Pick chỉ chọn các thuộc tính ở cấp ngoài cùng. Hãy đi vào kiểu lồng bên trong bằng indexed access type rồi chọn từ đó: Pick<User["address"], "city">. Để giữ cấu trúc lồng nhau, kết hợp chúng: Pick<User, "id"> & { address: Pick<User["address"], "city"> }.

Pick có giữ modifier readonly và tùy chọn không?

Có. Pick là một mapped type ánh xạ qua các khóa của T, nên thuộc tính nào là readonly hoặc tùy chọn trong T thì vẫn như vậy trong kiểu được chọn.

Hình minh họa các ngôn ngữ lập trình của Coddy

Học lập trình cùng Coddy

Bắt đầu