เจนเนอริกส์ - ไทป์สคริปต์ - การจำกัดชนิดของ T[K] ในฟังก์ชันเมื่อส่งค่าคีย์หลายค่าเข้ามา
ฉันกำลังพยายามเขียน hook ที่รับหลายคีย์ของอ็อบเจ็กต์และกรองข้อมูลตามตัวกรองที่ใช้งานอยู่ในปัจจุบัน ดังนั้น ประมาณตามแนวคิดนี้:
interface IFilterable<T,K extends keyof T & string>{
key: K;
customFilter?: (item: T[K], applied: T[K][]) => boolean
//The hook uses some default logic to filter if this function isn't available.
}
function useFilters<T,K extends keyof T & string>(data: T[], filters: IFilterable<T,K>[]): T[]{
//filter out the data, return new dataset.
}
ปัญหาคือเมื่อมีการส่งตัวกรองหลายตัว (IFilterable) เข้าไป พารามิเตอร์ item และ applied ในฟังก์ชัน customFilter จะกลายเป็นการรวมกันของประเภทของคีย์ หรือในโค้ด...
useFilters(
[{a: 1, b: "bob"], {a: 2, b: "berry"}],
[{key: "a", customFilter: (item, applied) => {
//Wanted: item has the type number
//Reality: item has the type (string | number)
},
{key: "b", customFilter: (item, applied) => {
//Wanted: item has the type string.
//Reality: item has the type (string | number)
}
}]);
ฉันได้ "แก้ไข" ปัญหาโดยการพิมพ์ทุกอย่างเป็น unknown (หรือ unknown[]) แล้วจึงสร้างฟังก์ชัน wrapper ที่แปลงสิ่งต่าง ๆ กลับไปเป็นประเภทที่ถูกต้องทีหลัง แต่ฉันต้องการที่จะให้ทุกอย่างยังคงมีการระบุประเภทอย่างเข้มงวดตลอดเส้นทางถ้าเป็นไปได้
ดังนั้น มีวิธีใดบ้างในกรณีแบบนี้ ที่ฉันกำลังส่งอาร์เรย์ของวัตถุซึ่งคุณสมบัติหนึ่งเป็นคีย์และอีกคุณสมบัติเป็นฟังก์ชันที่รับ T[K] เพื่อจำกัดให้เป็นชนิดของ T[K] เฉพาะ แทนที่จะเป็นชนิดสหสรรพคุณ?
ขอบคุณ
คำตอบ #1
อัปเดต 17 กันยายน 2021
ฉันทำการบ้านเสร็จแล้ว
พิจารณาตัวอย่างนี้:
/**
* Obtain a union of all values of a dictionary
*/
type Values<T> = T[keyof T]
type FilterBuilder<T extends any[]> = Values<{
[Key in keyof T[number]]: {
key: Key,
customFilter: (item: T[number][Key], applied: T[number][Key][]) => void
}
}>
{
// type Test = {
// key: "a";
// customFilter: (item: 1, applied: 1[]) => void;
// } | {
// key: "b";
// customFilter: (item: "str", applied: "str"[]) => void;
// }
type Test = FilterBuilder<[{ a: 1, b: 'str' }]>
}
const withFilters = <
Item extends Record<PropertyKey, unknown>,
Items extends Item[],
>(data: [...Items]) => <
Filter extends FilterBuilder<Items>,
Filters extends Filter[],
>(filters: [...Filters]) => { }
const useFilters = withFilters([{ a: 1, b: "bob" }, { a: 2, b: "berry" }])
const result = useFilters(
[{
key: "a", customFilter: (item, applied) => { }
},
{
key: "b", customFilter: (item, applied) => { }
}]
);
มันยากสำหรับ TypeScript ที่จะอนุมาน key ของวัตถุและนำประเภทนี้ไปใช้ทันทีกับคุณสมบัติอื่นของวัตุุจเดียวกัน อย่างน้อยในกรณีนี้
ดังนั้น ฉันจึงสร้างสหภาพของคู่ตัวกรอง key/customFilter ที่อนุญาตทั้งหมดขึ้นมา
สนามเด็กเล่น
Values - สร้างสหภาพของค่าทั้งหมดในโครงสร้างข้อมูลพจนานุกรม
ตัวสร้างตัวกรอง:
- ทำซ้ำสำหรับแต่ละคีย์ขององค์ประกอบที่มีอยู่ในทูเพิล
T - สร้างวัตถุใหม่สำหรับคีย์ที่วนซ้ำโดยมีพร็อพ
keyและcustomFilter - โดยที่
customFilterเป็นฟังก์ชันที่มีอาร์กิวเมนต์สองตัว:item- ค่าของอ็อบเจ็กต์ที่วนซ้ำ,applied- อาร์เรย์ของค่าของอ็อบเจ็กต์ที่วนซ้ำ
withFilters: เป็นฟังก์ชันแบบ curried มันคาดหวัง tuple ของวัตถุที่เราสนใจและคืนค่าฟังก์ชันซึ่งคาดหวังอาร์เรย์ของผลลัพธ์ FilterBuilder FilterBuilder เพียงแค่คืน union ที่มีการแยกประเภทของสถานะทั้งหมดที่อนุญาตของวัตถุ key/customFilter
