ทำไม typescript ไม่อนุญาตแบบวงกลมจะอ้างอิงไปยังอยู่ใน generics?

0

คำถาม

ที่นี่ตัวอย่างที่คนประเภทรอบโดยจะอ้างอิงไปยังตัวมันเองในของมัมายความว่าแต่เมื่อ abstracted ผ่านทางทั่วไปมันเลยจะล้มเหลว

type a = { val: a }; // <-- doesn't care about circular references!

type record<T> = { val: T };

type b = record<b>; // <-- doesn't work!

type func<T> = (arg: T) => void;

type c = func<c>; // <-- doesn't work!

type d = (arg: d) => void; // <-- works!?
types typescript
2021-11-23 20:48:45
2

คำตอบที่ดีที่สุด

3

เห็น ไมโครซอฟ/TypeScript#41164 สำหรับ canonical องตอบคำถามคำถามข้อนี้

TypeScript ทำ อนุญาตแบบวงกลมจะอ้างอิงไปยังอยู่ ทั่วไป ส่วนเชื่อมต่อ และ ทั่วไป เรียนตั้งแต่ส่วนเชื่อมต่อและชั้นเรียนโหมี statically รู้จักทรัพย์สิน/สมาชิก/วิธีการ กุญแจเองและดังนั้นพ circularity เกิดขึ้นใน"ปลอดภัย"สถานที่ชอบทรัพย์สิน มีค่าs หรือวิธีการพารามิเตอร์หรือกลับมาแบบนั้น

interface Interface<T> { val: T }
type X = Interface<X> // okay

class Class<T> { method(arg: T): void { } }
type Y = Class<Y> // okay

แต่สำหรับทั่วไป พิมพ์นามแฝง ไม่มีวันรับรองว่า. พิมพ์นามแฝงสามารถมีโครงสร้างนั่นไหนไม่ระบุชื่อประเภทจะต้องดังนั้นศักยภาพเพียงพอที่ circularity ไม่ constrained จะ recursive ต้นไม้-เหมือนวัตถุ:

type Safe<T> = { val: T };
type Unsafe<T> = T | { val: string };

ตอนที่คอมไพเลอร์ instantiates เป็นประเภททั่วไปมั defers ของมันประเมินผลมันยังไม่ทันทีเลยพยายามเต็มที่คำนวณผลลัพธ์จากประเภทนี้ได้นะ ทั้งหมดที่มันมองเห็นเป็นรูปแบบ:

type WouldBeSafe = Safe<WouldBeSafe>; 
type WouldBeUnsafe = Unsafe<WouldBeUnsafe>; 

ทั้งสองของพวกนั้นดูเหมือนกัไปที่คอมไพเลอร์... type X = SomeGenericTypeAlias<X>. มันไม่สามารถ"เห็น"นั่น WouldBeSafe ไม่เป็นไรหรอ:

//type WouldBeSafe = { val: WouldBeSafe }; // would be okay

ในขณะที่ WouldBeUnsafe อาจจะเป็นปัญหา:

//type WouldBeUnsafe = WouldBeUnsafe | { val: string }; // would be error

ตั้งแต่มันไม่สามารถเห็นความแตกต่าง,และเพราะอย่างน้อยก็บางอย่า usages นจะเป็นอย่างผิดกฏหมายแบบวงกลมมันแค่ prohibits พวกเขาทั้งหมด


ดังนั้น,คุณสามารถทำอะไรได้บ้าง? นี่คือหนึ่งในพวกนั้นคดีที่ฉันจะขอแนะนำให้ใช้ interface แทนที่จะเป็น type ตอนที่คุณทำได้ คุณสามารถเขียนใหม่ของคุณ record ชนิด(การเปลี่ยนมัน MyRecord สำหรับการตั้งชื่อประชุมด้วยเหตุผล)เป็นข้ interface และทุกอย่างที่จะทำงาน:

interface MyRecord<T> { val: T };
type B = MyRecord<B>; // okay

คุณสามารถแม้แต่จะเขียนใหม่ของคุณ func ชนิด(การเปลี่ยนมัน Func สำหรับการตั้งชื่อประชุมด้วยเหตุผลอีกครั้ง)เป็นข้ interface โดยการเปลี่ยน ฟังก์ชันประเภทรแสดง ไวยากรณ์เข้าไปใน โทรลายเซ็นต์รูปแบบการสั่งงาน:

interface Func<T> { (arg: T): void }
type C = Func<C>; // okay

แน่นอนมีสถานการณ์ที่คุณไม่สามารถทำอย่างนั้นโดยตรงอย่างเช่นที่สร้าง Record เครื่องมือประเภท:

type Darn = Record<string, Darn>; // error

และคุณไม่สามารถเขียนใหม่ใน แผนไว้ประเภท Record เป็นข้ interface. และแน่นอนมันคงจะไม่ปลอดภัยที่จะพยายามที่จะทำให้กุญแจแบบวงกลมเหมือน type NoGood = Record<NoGood, string>. ถ้าเพียงแต่คุณต้องการจะทำ Record<string, T> สำหรับทั่วไป Tคุณ สามารถ เขียนใหม่ มัน เป็น interface:

interface Dictionary<T> extends Record<string, T> { };
type Works = Dictionary<Works>;

ดังนั้นมันค่อนข้างบ่อยนักหนทางที่จะใช้เป็น interface แทนที่จะเป็น type เพื่ออนุญาตให้คุณต้องแสดงถึงความสน"ปลอดภัย"recursive ยชนิด

สนามเด็กเล่นเชื่อมโยงไปยังนรหัส

2021-11-23 21:31:48

ขอบคุณ! นี่มันเจ๋งมาและช่วยได้!
radiish
1

ปล่อยให้เป็นผ่าพวกนี้สถานการณ์เป็นหนึ่งโดยหนึ่ง

สถานการณ์ 1

type a = { val: a }; // <-- doesn't care about circular references!

มันน่าสนใจนี่คือได้รับอนุญาต ฉันไม่เห็นว่าคุณอาจจะสามารถสร้างการตัวอย่างเช่นนั้นจะพอใจนี่ประเภท:

const A: a = {
  val: {
    val: {
      // It will always error out at the most inner node.
    }
  }
}

สถานการณ์ 2

type record<T> = { val: T };

นี่ไม่ใช่การอ้างอิงแบบวงกลมและเป็นที่พึงพอใจเหมือนนี้:

const B: record<string> = {
  val: "test"
}

สร้างสถานการณ์จำลอง 3

type b = record<b>; // <-- doesn't work!

มันสมเหตุสมผลกับฉันว่าเรื่องนี้ไม่ได้ผลหรอก เหมือนอยู่ในสถานการณ์ 1 อาจจะไม่มีทางที่จะสร้างเป็นตัวอย่างเช่นนั้นนี่มั constraint.

สถานการณ์ 4

type func<T> = (arg: T) => void;

นี่ไม่ใช่การอ้างอิงแบบวงกลมและเป็นที่พึงพอใจเหมือนนี้:

const C: func<string> = (arg: string) => {}

สร้างสถานการณ์จำลอง 5

type c = func<c>; // <-- doesn't work!

มันสมเหตุสมผลกับฉันว่าเรื่องนี้ไม่ได้ผลหรอก เหมือนอยู่ในสถานการณ์ 1 อาจจะไม่มีทางที่จะสร้างเป็นตัวอย่างเช่นนั้นนี่มั constraint.

สร้างสถานการณ์จำลอง 6

type d = (arg: d) => void; // <-- works!?

ฉันสามารถเขียนฟังก์ชันต้องเรีนี้ constraint แต่ฉันไม่แน่ใจว่ามันจะช่วยให้ฉัน:

const D: d = (arg) => {}
D(D)
2021-11-23 21:34:19

ในภาษาอื่นๆ

หน้านี้อยู่ในภาษาอื่นๆ

Русский
..................................................................................................................
Italiano
..................................................................................................................
Polski
..................................................................................................................
Română
..................................................................................................................
한국어
..................................................................................................................
हिन्दी
..................................................................................................................
Français
..................................................................................................................
Türk
..................................................................................................................
Česk
..................................................................................................................
Português
..................................................................................................................
中文
..................................................................................................................
Español
..................................................................................................................
Slovenský
..................................................................................................................

ดังอยู่ในนี้หมวดหมู่

ดังคำถามอยู่ในนี้หมวดหมู่