De ce mașina de scris nu permite referințe circulare în generics?

0

Problema

aici exemplele în cazul în care tipul direct referințe în sine, în definiția sa, dar când captate printr-un generic complet eșuează.

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

Cel mai bun răspuns

3

Vezi microsoft/Script#41164 pentru un canonic răspunsul la această întrebare.

Mașina de scris nu permite referințe circulare în generic interfețe și generic clase, deoarece interfețele și cazuri de clasă au static cunoscut de proprietate/membru/metoda - cheies și deci orice circularitate se întâmplă în locuri "sigure", ca pe o proprietate de valoares sau metoda de parametri sau de tipul de retur.

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

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

Dar pentru generice de tip alias-uri nu există nici o astfel de garanție. Tip pseudonime poate avea orice structură care orice anonim de tip poate fi, deci, potențialul de circularitate nu este constrâns să recursiv copac, cum ar fi obiecte:

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

Atunci când compilatorul instanțiază un tip generic, nu se supune evaluării sale; nu încercați imediat să complet calcula rezultă tip. Toate se vede este de forma:

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

Ambele arata la fel de compilator... type X = SomeGenericTypeAlias<X>. Acesta nu poate "vedea" că WouldBeSafe ar fi bine:

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

în timp ce WouldBeUnsafe ar fi o problema:

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

Deoarece nu pot vedea diferența, și pentru că, cel puțin unele utilizări ar fi ilegal circulară, doar interzice tot de pe ei.


Deci, ce puteți face? Aceasta este una dintre acele cazuri în care aș sugera folosind interface în loc de type când poți. Poți să-ți rescrii record tip (schimba-l la MyRecord pentru convenție de denumire motive) ca un interface și totul se va rezolva:

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

Puteți chiar să-ți rescrii func tip (schimba-l la Func pentru convenție de denumire motive din nou) ca un interface prin schimbarea funcției de tip expresie sintaxa într-un apel semnătură sintaxa:

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

Desigur, există situații în care nu poți face asta în mod direct, cum ar fi built-in Record tip de utilitate:

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

și nu poți rescrie mapate tip Record ca un interface. Și într-adevăr, ar fi nesigure pentru a încerca să facă cheile circulară, ca type NoGood = Record<NoGood, string>. Dacă doriți doar pentru a face Record<string, T> pentru generic T, vă poate rescrie asta ca un interface:

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

Deci, există destul de multe ori o modalitate de a utiliza un interface în loc de type pentru a vă permite să-și exprime "în siguranță" recursive tipuri.

Loc de joaca link-ul de la cod

2021-11-23 21:31:48

multumesc!!! acest lucru este cool și util!
radiish
1

Hai să disecăm aceste scenariul e unul câte unul.

Scenariul 1

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

E interesant acest lucru este permis. Nu văd cum ai putea fi capabil de a construi o instanță care ar satisface acest tip:

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

Scenariul 2

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

Aceasta nu este o referință circulară și pot fi satisfăcute astfel:

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

Scenariul 3

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

Se face sens pentru mine că acest lucru nu funcționează. La fel ca și în Scenariul 1, nu ar fi nici o modalitate de a construi o instanță care satisface această constrângere.

Scenariul 4

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

Aceasta nu este o referință circulară și pot fi satisfăcute astfel:

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

Scenariul 5

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

Se face sens pentru mine că acest lucru nu funcționează. La fel ca și în Scenariul 1, nu ar fi nici o modalitate de a construi o instanță care satisface această constrângere.

Scenariul 6

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

Nu pot scrie de fapt o funcție pentru a satisface această constrângere, dar nu sunt sigur ce e cu mine:

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

În alte limbi

Această pagină este în alte limbi

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