Prečo strojom nebolo možné, kruhové odkazy v generiká?

0

Otázka

tu príklady, kde typu priamo odkazy sám v jej definícii, ale keď odoberanej cez všeobecný to úplne nepodarí.

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

Najlepšiu odpoveď

3

Pozri microsoft/Strojom#41164 pre canonical odpoveď na túto otázku.

Záznam sa povoliť kruhové odkazy v generické rozhranie a generické triedy, od rozhrania a triedy prípadoch majú staticky známe majetok/člen/metóda tlačidlos a preto všetky circularity sa deje v "bezpečí" miesta, ako sú napríklad majetkové hodnotys alebo parametrov metódy alebo návrat typ.

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

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

Ale pre všeobecný typ prezývky nie je tam žiadna taká zaručiť. Typ prezývky môže mať štruktúru, že akékoľvek anonymné typu môže mať, tak potenciál circularity nie je fixovaný na rekurzívne strom-ako objekty:

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

Keď kompilátor instantiates všeobecný typ, to odkladá jej vyhodnotenie; to nie je bezprostredne skúste plne vypočítať výsledný typ. Všetko to vidí je formulár:

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

Obe tieto vyzerajú rovnako zostavovateľ... type X = SomeGenericTypeAlias<X>. To nemôže "vidieť", ktoré WouldBeSafe bude v poriadku:

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

zatiaľ čo WouldBeUnsafe by byť problém:

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

Pretože to nemôže vidieť rozdiel, a keďže aspoň niektoré zvyklosti by byť nelegálne kruhové, to len zakazuje všetky z nich.


Takže, čo môžete urobiť? To je jeden z tých prípadov, kde by som navrhnúť pomocou interface namiesto type keď môžete. Môžete prepísať vaše record typ (zmenou na MyRecord pre konvencia dôvodov) ako interface a všetko bude fungovať:

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

Dokonca môžete prepísať vaše func typ (zmenou na Func pre konvencia dôvodov znova) ako interface zmenou funkcie typu prejavu syntax do hovoru podpis syntax:

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

Samozrejme, sú situácie, kde si nemôže robiť priamo, ako je napríklad vstavaný Record pomôcka typ:

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

a nemôžete prepísať mapované typ Record ako interface. A naozaj, to by bolo nebezpečné sa snažiť, aby sa tlačidlá kruhová, ako type NoGood = Record<NoGood, string>. Ak si len chcete urobiť Record<string, T> pre generické Tsi môžete prepísať , že ako interface:

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

Takže tam nie je dosť často, tak na použitie interface namiesto type aby ste mohli express "bezpečné" rekurzívne typy.

Ihrisko odkaz na kód

2021-11-23 21:31:48

vďaka! je to cool a užitočné!
radiish
1

Poďme podrobne preskúmať tieto scenár je po jednom.

Scenár 1

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

Je zaujímavé, je to povolené. Nevidím, ako by ste mohli byť schopný postaviť napríklad, že by splnenie tohto typu:

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

Scenár 2

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

Nie je to zacyklený odkaz a môžete byť spokojní, ako je tento:

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

Scenár 3

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

To dáva zmysel, že to nefunguje. Rovnako ako v Scenári 1, tam by bol žiadny spôsob, ako budovať inštanciu, ktorá spĺňa požiadavky tohto obmedzenia.

Scenár 4

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

Nie je to zacyklený odkaz a môžete byť spokojní, ako je tento:

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

Scenár 5

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

To dáva zmysel, že to nefunguje. Rovnako ako v Scenári 1, tam by bol žiadny spôsob, ako budovať inštanciu, ktorá spĺňa požiadavky tohto obmedzenia.

Scenár 6

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

Som si vlastne napísať funkciu pre splnenie tohto obmedzenia, ale nie som si istý, čo to dostať mne:

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

V iných jazykoch

Táto stránka je v iných jazykoch

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