จาวาสคริปต์ - ประเภทลูกขององค์ประกอบในอาเรย์
คำถามเกี่ยวกับการสร้างประเภทจากโครงสร้างข้อมูล ฉันมีอาเรย์แบบนี้:
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 */
}
}}
>
