typescript - การใช้ฟังก์ชันตัวตกแต่งทำให้เกิดข้อผิดพลาดประเภทการอ้างอิงวงกลม
โค้ดของฉันทำงานได้ดีจนกระทั่งฉันพยายามห่อการนิยามฟังก์ชันด้วยเดคอเรเตอร์ (โดยที่ฉันหมายถึง 'เดคอเรเตอร์' ว่าเป็นฟังก์ชันระดับสูงที่รับฟังก์ชันหนึ่งเป็นพารามิเตอร์และคืนค่าฟังก์ชันอีกตัวหนึ่ง, ฉันไม่ได้พูดถึงไวยากรณ์เดคอเรเตอร์ที่จะมาในอนาคต) เมื่อฉันใช้เดคอเรเตอร์นี้ ฉันได้รับข้อผิดพลาด Type alias 'Thing' circularly references itself เหตุใดสิ่งนี้ถึงเกิดขึ้น?
ด้านล่างนี้คือตัวอย่างโค้ดที่เสียหาย (นี่คือเวอร์ชันที่เรียบง่ายอย่างยิ่งที่พยายามเพียงแค่แสดงปัญหาที่ฉันกำลังพบเจอ)
const decorate = (fn: () => boolean) => fn
const thing = {
fn: decorate(() => {
type Thing = typeof thing
const thing_: Thing = thing
return 'fn' in thing_
})
}
เปรียบเทียบกับโค้ดตัวอย่างด้านล่างนี้ ซึ่งทำงานได้ดี ฉันแค่ลบการเรียกใช้ decorate (ซึ่งในตัวอย่างที่เรียบง่ายนี้เป็นเพียงฟังก์ชันเอกลักษณ์)
const decorate = (fn: () => boolean) => fn
const thing = {
fn: () => {
type Thing = typeof thing
const thing_: Thing = thing
return 'fn' in thing_
}
}
ฉันสามารถนำตัวอย่างที่เสียของฉันมาและแทนที่ฟังก์ชัน decorate ด้วยฟังก์ชันทั่วไปดังต่อไปนี้ได้ แล้วมันก็จะเริ่มทำงานได้อย่างมหัศจรรย์ แม้ว่าสองคำนิยามนี้ควรจะมีฟังก์ชันเหมือนกันในลักษณะเฉพาะที่ถูกนำมาใช้ก็ตาม
const decorate = <T>(fn: T): T => fn
อัปเดต: สำหรับใครที่สนใจ นี่คือวิธีที่ฉันจัดการแก้ไขปัญหาของฉัน
ฉันต้องบอก TypeScript อย่างชัดเจนว่าประเภทของฟังก์ชันของฉันคืออะไร ดังนั้นฉันจึงใส่มันใน IIFE และกำหนดให้กับตัวแปรที่มีการระบุประเภทไว้ แบบนี้:
const decorate = (fn: () => boolean) => fn
const thing = {
fn: (() => {
type MyFnType = () => boolean
const fn: MyFnType = decorate(() => {
type Thing = typeof thing
const thing_: Thing = thing
return 'fn' in thing_
})
return fn
})()
}
มันไม่สวยหรอก แต่ก็ช่วยแก้ปัญหานี้ได้
(โปรดทราบว่านี่ไม่ใช่คำตอบของคำถามของตัวเองของฉัน เพราะคำถามของฉันถามว่าเหตุใดฉันต้องทำวิธีแก้ปัญหาแบบนี้ ไม่ใช่ว่าฉันกำลังถามวิธีทำมัน)
คำตอบ #1
มาลองเขียนแบบนี้กัน
const decorate = (fn: () => boolean) => fn
const t = () => {
type Thing = typeof thing
const thing_: Thing = thing
return 'fn' in thing_
};
ตอนนี้ถ้าคุณเขียน
const thing = {
fn: decorate(t)
}
เนื่องจากสิ่งนี้เกี่ยวข้องกับการเรียกใช้ decorate โดยมี t เป็นอาร์กิวเมนต์ คอมไพเลอร์ จำเป็นต้อง ให้ t สามารถกำหนดค่าให้กับ ()=>boolean ก่อนที่จะสามารถดำเนินการอนุมานชนิดของ thing ดังนั้นมันต้องตรวจสอบชนิดของ t ก่อนที่จะมีการอนุมานชนิดของ thing ซึ่งทำให้เกิดการอ้างอิงแบบวงกลม กล่าวคือ ในกรณีนี้ ชนิดของ thing สำคัญต่อ t และชนิดของ t ก็สำคัญต่อ thing ด้วย
ในทางตรงกันข้ามเมื่อคุณเขียน
const thing = {
fn: t
}
ไม่มีข้อกำหนดเกี่ยวกับประเภทที่ t ควรจะเป็น ตัวคอมไพเลอร์สามารถดำเนินการในการสรุปประเภทของ thing ได้ เพียงแค่สรุปประเภทของ fn ให้เป็นประเภทใดก็ได้ของ t โดยไม่ต้องสนใจว่ามันเป็นอะไรจริง ๆ ดังนั้นจึงไม่จำเป็นต้องตรวจสอบประเภทของ t ล่วงหน้า ดังนั้นในกรณีนี้ประเภทของ thing มีความสำคัญต่อ t แต่ประเภทของ t ไม่สำคัญต่อ thing
คำตอบ #2
สุดท้ายฉันก็ถามเรื่องนี้ในที่เก็บข้อมูลของ TypeScript (สามารถแวะไปดูได้ที่นี่ ฟังดูเหมือนเหตุผลที่ TypeScript ไม่สามารถกำหนดประเภทโดยอัตโนมัติในตัวอย่างโค้ดของฉันนั้นสรุปได้ว่าการทำเช่นนั้นจะต้องมีการเขียนคอมไพเลอร์ของ TypeScript ใหม่ทั้งหมด และเพิ่มความซับซ้อนมากมายให้กับมัน สำหรับสิ่งที่พวกเขารู้สึกว่าไม่จำเป็นพอที่จะต้องมีการเขียนใหม่และเพิ่มความซับซ้อนแบบนี้ ซึ่งเป็นเรื่องที่เข้าใจได้แน่นอน
สามารถดูที่ลิงก์เพื่อดูการสนทนาตรงได้เลย
