TypeScript Interface Function Field ข้อผิดพลาดประเภท Contravariance

Sep 08 2020

ฉันมีประเภทต่อไปนี้ซึ่งกำหนดว่าคุณสมบัติทั้งหมดจะเป็นฟังก์ชันซึ่งไม่ยอมรับข้อโต้แย้งใด ๆ หรืออาร์กิวเมนต์ประเภทเดียว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 ดูเหมือนว่าการ จำกัด นี้ควรได้รับอนุญาต

ความช่วยเหลือใด ๆ ที่จะได้รับการชื่นชมอย่างมาก. ขอขอบคุณ!

คำตอบ

1 jcalz Sep 08 2020 at 11:24

นี่คือตัวอย่างของประเภทฟังก์ชั่นเป็น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()ล้มเหลว: ความสามารถในการทดแทน

ลิงก์สนามเด็กเล่นไปยังรหัส