ค้นหาเว็บไซต์

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 ใหม่ทั้งหมด และเพิ่มความซับซ้อนมากมายให้กับมัน สำหรับสิ่งที่พวกเขารู้สึกว่าไม่จำเป็นพอที่จะต้องมีการเขียนใหม่และเพิ่มความซับซ้อนแบบนี้ ซึ่งเป็นเรื่องที่เข้าใจได้แน่นอน

สามารถดูที่ลิงก์เพื่อดูการสนทนาตรงได้เลย