วัตถุประสงค์ของ bivarianceHack ในประเภทของ TypeScript คืออะไร?
ขณะอ่านประเภทของ TypeScript สำหรับ React ฉันเห็นการใช้งานรูปแบบนี้บางอย่างที่เกี่ยวข้องกับการประกาศฟังก์ชัน bivarianceHack():
@types/react/index.d.ts
type EventHandler<E extends SyntheticEvent<any>> = { bivarianceHack(event: E): void }["bivarianceHack"];
การค้นหาไม่ได้พาฉันไปยังเอกสารใด ๆ ที่อธิบายว่าทำไมจึงใช้รูปแบบเฉพาะนี้ แม้ว่าฉันจะพบกรณีอื่นของรูปแบบนี้ที่ถูกใช้อยู่ ดังนั้นดูเหมือนว่าจะไม่ใช่รูปแบบเฉพาะของ React
ทำไมจึงใช้รูปแบบนี้แทนที่จะเป็น (event: E) => void?
คำตอบ #1
เรื่องนี้เกี่ยวข้องกับความเข้ากันได้ของฟังก์ชันภายใต้ตัวเลือก strictfunctionTypes ภายใต้ตัวเลือกนี้ หากอาร์กิวเมนต์เป็นชนิดที่สืบทอดมา คุณจะไม่สามารถส่งมันไปยังฟังก์ชันที่รับอาร์กิวเมนต์เป็นคลาสฐานได้ ตัวอย่างเช่น:
class Animal { private x:undefined }
class Dog extends Animal { private d: undefined }
type EventHandler<E extends Animal> = (event: E) => void
let o: EventHandler<Animal> = (o: Dog) => { } // fails under strictFunctionTypes
อย่างไรก็ตาม มีข้อควรระวังเกี่ยวกับประเภทฟังก์ชันที่เข้มงวด ซึ่งระบุไว้ใน PR
การตรวจสอบที่เข้มงวดมากขึ้นจะใช้กับประเภทฟังก์ชันทั้งหมด ยกเว้นฟังก์ชันที่มาจากการประกาศเมธอดหรือคอนสตรัคเตอร์ เมธอดถูกยกเว้นโดยเฉพาะเพื่อให้แน่ใจว่าคลาสเจเนอริกและอินเทอร์เฟซ (เช่น
Array<T>) ยังคงสัมพันธ์แบบโคแวเรียนต์เป็นส่วนใหญ่ ผลกระทบจากการตรวจสอบเมธอดอย่างเข้มงวดจะทำให้เกิดการเปลี่ยนแปลงที่ใหญ่ขึ้นมาก เนื่องจากจำนวนประเภทเจเนอริกจำนวนมากจะกลายเป็น invariant (แม้กระนั้น เรายังคงอาจสำรวจโหมดเข้มงวดนี้ต่อไป)
เน้นข้อความเพิ่มเติม
ดังนั้นบทบาทของการปรับแก้คือเพื่ออนุญาตให้มีพฤติกรรมแบบทวิแปรของ EventHandler แม้จะอยู่ภายใต้ strictFunctionTypes ก็ตาม เนื่องจากลายเซ็นของตัวจัดการเหตุการณ์จะมีต้นกำเนิดมาจากประกาศเมธอด มันจะไม่ต้องอยู่ภายใต้การตรวจสอบฟังก์ชันที่เข้มงวดมากขึ้น
type BivariantEventHandler<E extends Animal> = { bivarianceHack(event: E): void }["bivarianceHack"];
let o2: BivariantEventHandler<Animal> = (o: Dog) => { } // still ok under strictFunctionTypes
ลิงก์สนามเด็กเล่น
คำตอบ #2
ดังที่ได้กล่าวไปแล้ว นี่เป็นวิธีการแก้ไขปัญหาของ --strictFunctionTypes ซึ่งส่งผลต่อความสามารถในการกำหนดประเภทของฟังก์ชันกับพารามิเตอร์ของมัน
ฉันคิดว่ามันคุ้มค่าที่จะแสดงสิ่งนี้เพิ่มเติม
ตัวอย่าง
สนามเด็กเล่น
คำอธิบาย
'foo' | 'bar' เป็นซับไทป์ของ string หรือใน TypeScript คุณสามารถพูดได้ว่า 'foo' | 'bar' extends string
เมื่อใช้เป็นพารามิเตอร์กับฟังก์ชัน F ความสามารถในการกำหนดจะถูกกลับด้าน
F<string> extends F<'foo' | 'bar'> ถูกต้อง ซึ่งแสดงให้เห็นถึงความต้านทานการแปรสภาพ (contravariance) บนพารามิเตอร์ชนิดข้อมูล
ในตัวอย่างข้างต้น มีการทดสอบสองแบบ สำหรับฟังก์ชันโดยตรง และจากนั้นก็ทดสอบโดยใช้วิธีการแก้ไข bivariance แบบแรกทดสอบ contravariance และแบบที่สองทดสอบ covariance
หากไม่ใช้การแฮ็ก ฟังก์ชันจะเป็นเพียงตัวแปรตรงข้ามเท่านั้น แต่หลังจากใช้การแฮ็กแล้ว มันจะเป็นทั้งตัวแปรตรงข้าม และตัวแปรตรงกัน ดังนั้นเราสามารถบอกได้ว่ามันเป็น ตัวแปรสองทาง
มีบทความที่ดีมากเกี่ยวกับแนวคิดนี้โดยเฉพาะใน TypeScript ซึ่งอธิบายได้ดีกว่าที่ฉันสามารถทำได้ที่นี่
