ค้นหาเว็บไซต์

เจนเนอริกส์ - ไทป์สคริปต์ - การจำกัดชนิดของ 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 - สร้างสหภาพของค่าทั้งหมดในโครงสร้างข้อมูลพจนานุกรม

ตัวสร้างตัวกรอง:

  1. ทำซ้ำสำหรับแต่ละคีย์ขององค์ประกอบที่มีอยู่ในทูเพิล T
  2. สร้างวัตถุใหม่สำหรับคีย์ที่วนซ้ำโดยมีพร็อพ key และ customFilter
  3. โดยที่ customFilter เป็นฟังก์ชันที่มีอาร์กิวเมนต์สองตัว: item - ค่าของอ็อบเจ็กต์ที่วนซ้ำ, applied - อาร์เรย์ของค่าของอ็อบเจ็กต์ที่วนซ้ำ

withFilters: เป็นฟังก์ชันแบบ curried มันคาดหวัง tuple ของวัตถุที่เราสนใจและคืนค่าฟังก์ชันซึ่งคาดหวังอาร์เรย์ของผลลัพธ์ FilterBuilder FilterBuilder เพียงแค่คืน union ที่มีการแยกประเภทของสถานะทั้งหมดที่อนุญาตของวัตถุ key/customFilter