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