TypeScript Interface Function Field ข้อผิดพลาดประเภท Contravariance
ฉันมีประเภทต่อไปนี้ซึ่งกำหนดว่าคุณสมบัติทั้งหมดจะเป็นฟังก์ชันซึ่งไม่ยอมรับข้อโต้แย้งใด ๆ หรืออาร์กิวเมนต์ประเภทเดียวRecord<string, any>:
type FnTrait = Record<
string,
(input?: Record<string, any>) => any
>;
ฉันพยายามขยายประเภทนี้ไปยังอินเทอร์เฟซอื่น (ซึ่งฉันต้องการให้มีข้อ จำกัด เหมือนกัน)
interface HasFn extends FnTrait {
someFn(): string; // no problem
anotherFn(o: {id: string}): number; // ts error 2411
}
สิ่งนี้ก่อให้เกิดข้อผิดพลาดในanotherFn:Property 'anotherFn' of type '(o: { id: string; }) => number' is not assignable to string index type '(input?: Record<string | number | symbol, any> | undefined) => any'.
เหตุใดจึงsomeFnไม่มีข้อผิดพลาดในขณะที่anotherFnสร้างข้อผิดพลาด ts 2411 ดูเหมือนว่าการ จำกัด นี้ควรได้รับอนุญาต
ความช่วยเหลือใด ๆ ที่จะได้รับการชื่นชมอย่างมาก. ขอขอบคุณ!
คำตอบ
นี่คือตัวอย่างของประเภทฟังก์ชั่นเป็นcontravariantในพารามิเตอร์ของพวกเขาคำว่า "contravariant" หมายถึง "แตกต่างกันไปในทางตรงกันข้าม" ถ้าคุณทำพารามิเตอร์เพิ่มเติมฟังก์ชั่นที่เฉพาะเจาะจง ( จำกัด ) คุณจะทำให้ฟังก์ชั่นพิมพ์ตัวเองมากขึ้นทั่วไป (จอกว้าง) นั่นหมายความว่าแทนที่จะทำให้ชนิดย่อยของ(ซึ่งเป็นสิ่งที่หมายถึง) คุณเป็นประเภททำให้มันเป็นsupertypeนี้ไม่ได้รับอนุญาตและละเมิดหลักการของการทดแทนHasFnFnTraitextends
โดยเฉพาะอย่างยิ่งanotherFn()มีข้อผิดพลาดเนื่องจากต้องการให้อาร์กิวเมนต์มีคุณสมบัติstring-valued idในขณะที่FnTrait["anotherFn"]ไม่มี คาดว่าคุณสามารถเรียกคุณสมบัติใด ๆ ของ a FnTraitโดยไม่มีพารามิเตอร์หรือด้วยพารามิเตอร์เดียวเกี่ยวกับประเภทใดก็ได้ แต่HasFnอาจระเบิดได้หากคุณเรียกanotherFn()ใช้เมธอดโดยไม่มีพารามิเตอร์หรือพารามิเตอร์ไม่มีidคุณสมบัติที่ถูกต้อง ดังนั้นตามที่กำหนดไว้HasFnจึงไม่สามารถกำหนดให้FnTraitได้แม้ว่าจะมีการประกาศให้ขยาย:
const hasFn: HasFn = {
someFn: () => "",
anotherFn: o => o.id.length
}
const fnTrait: FnTrait = hasFn;
fnTrait.anotherFn({ a: 123 }); // okay at compile time, explodes at runtime
เนื่องจากanotherFn()หมายความว่าคุณไม่สามารถแทนที่FnTraitค่าที่HasFnร้องขอค่าได้อย่างปลอดภัยไม่สามารถFnTraitกำหนดให้HasFnได้และคุณได้รับข้อผิดพลาด
เหตุผลว่าทำไมsomeFn()เป็นไม่ผิดพลาดในการเป็นเพราะเป็นหน้าที่ของพารามิเตอร์ที่น้อยลงคือการมอบหมายฟังก์ชั่นที่ใช้พารามิเตอร์มากขึ้น นี่เป็นเพราะsomeFn()โดยความจำเป็นจะละเว้นพารามิเตอร์ใด ๆ ที่ส่งเข้ามาดังนั้นจึงปลอดภัยที่จะถือว่าเป็นฟังก์ชันซึ่งอาจได้รับพารามิเตอร์
fnTrait.someFn({ a: 123 }); // okay at compile time and runtime
สิ่งนี้ใช้ได้กับสาเหตุเดียวกันที่anotherFn()ล้มเหลว: ความสามารถในการทดแทน
ลิงก์สนามเด็กเล่นไปยังรหัส