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

จาวาสคริปต์ - ประเภทลูกขององค์ประกอบในอาเรย์


คำถามเกี่ยวกับการสร้างประเภทจากโครงสร้างข้อมูล ฉันมีอาเรย์แบบนี้:

interface ICVSection {
  namespace: string;
  validation: Yup.ObjectSchema<any>;
}

const CVSections: ICVSection[] = [
  {
    namespace: 'common',
    validation: CommonValidationSchema,
  },
  {
    namespace: 'summary',
    validation: SummarySchema,
  }
  /* ... */
};

ดังนั้นคำถามคือ: เป็นไปได้หรือไม่ที่จะใช้ข้อมูลข้างต้นสร้างประเภทดังต่อไปนี้:

type Schema = {
  common: typeof CommonValidationSchema,
  namespace: typeof SummarySchema,
  /* ... */
}

ฉันไม่แน่ใจ 100% ว่ามันเป็นไปได้ใน TypeScript หรือไม่ แต่ถ้าเป็นไปได้ จะดีมากถ้ามีใครสักคนสามารถพิสูจน์ว่าฉันถูกหรือผิด

คำตอบ #1

ดังนั้นนี่คือวิธีแก้ปัญหาที่ฉันคิดออกมา:

type namespaces = typeof CVSections[number]['namespace'];

type Schema = {
  [K in namespaces]: Pick<Extract<typeof CVSections[number], { namespace: K }>, 'validation'>['validation']>;
};

แต่เพื่อให้มันทำงานได้ เราจำเป็นต้องปรับการกำหนดอาร์เรย์ของเราเล็กน้อย แทนที่จะพิมพ์มันเป็น ICVSection[] เราต้องกำหนดมันเป็น const CVSections=[ /* ... */ ] as const; เพื่อให้ TypeScript สามารถจำกัดค่าที่เป็นไปได้ของ namespaces ได้

ในกรณีของฉัน เนื่องจากฉันกำลังใช้ไลบรารี Yup ร่วมกับ Formik สำหรับ schema การตรวจสอบความถูกต้อง ฉันสามารถนำการแปลงประเภทเพิ่มเติมมาใช้เพื่อเปิดตัวประเภท Schema ของ Yup ให้เป็นประเภท TypeScript แบบธรรมดาได้ วิธีแก้ปัญหาสุดท้ายจะมีลักษณะดังนี้:

type Schema = {
  [K in namespaces]: Yup.InferType<Pick<Extract<typeof CVSections[number], { namespace: K }>, 'validation'>['validation']>;
};

จากนั้นเราสามารถใช้ประเภทนี้กับคอมโพเนนต์ <Formik> เพื่อบังคับการตรวจสอบประเภท:

<Formik<Schema>
  initialValues={{
    common: {
      /* mandatory values */
    },
    summary: {
      /* mandatory values */
    }
  }}
>