Proč strojopis neumožňuje cyklické odkazy v generika?

0

Otázka

zde příklady, kde je tento typ přímo odkazy sám v jeho definici, ale když získávána prostřednictvím generických to zcela selže.

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

Nejlepší odpověď

3

Viz microsoft/Strojopis#41164 pro kanonické odpověď na tuto otázku.

Strojopis povolit cyklické odkazy v generické rozhraní a generických tříd, protože rozhraní a instance třídy mají staticky známá vlastnost/člen/metoda klávesys, a tak žádné kruhovitosti se stane v "bezpečných" místech, jako je majetkové hodnotynebo parametry metody nebo návratový typ.

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

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

Ale pro obecný typ aliasy není tam žádná taková záruka. Typ aliasy mohou mít jakoukoli strukturu, že jakékoliv anonymní typ, takže potenciál cirkulace není omezen na rekurzivní strom-jako objekty:

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

Když kompilátor vytváří instance obecný typ, je odkládá jeho hodnocení; to není okamžitě snažit, aby plně vypočítat výsledný typ. Všichni to vidí, je formě:

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

Oba tyto stejné kompilátor... type X = SomeGenericTypeAlias<X>. To nemůže "vidět", že WouldBeSafe by být v pořádku:

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

zatímco WouldBeUnsafe by být problém:

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

Protože nemůže vidět rozdíl, a protože alespoň některé zvyklosti by být nelegálně kruhové, jen zakazuje všechny z nich.


Takže, co můžete dělat? To je jeden z těch případů, kdy bych navrhnout pomocí interface místo type když můžete. Můžete přepsat své record typ (mění ji na MyRecord pro pojmenování důvodů) jako interface a vše bude fungovat:

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

Můžete dokonce přepsat func typ (mění ji na Func pro pojmenování důvodů opět) jako interface změnou funkce typ výrazu syntaxe do volání podpis syntaxe:

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

Samozřejmě existují situace, kdy můžete to udělat přímo, jako je například vestavěný Record utility typu:

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

a nemůžeš přepsat mapovány typ Record jako interface. A opravdu, to by bylo nebezpečné, aby se pokusili udělat klíče kruhové, jako type NoGood = Record<NoGood, string>. Pokud si jen chcete udělat Record<string, T> pro generic T, můžete přepsat , že jako interface:

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

Takže tam je docela často, způsob využití interface místo type umožňují vyjádřit "bezpečný" rekurzivní typy.

Hřiště odkaz na kód

2021-11-23 21:31:48

díky!!! to je v pohodě a užitečné!
radiish
1

Pojďme pitvat tyto scénáře jeden po druhém.

Scénář 1

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

Je zajímavé, že je to povoleno. Nechápu, jak mohl být schopen sestavit instanci, který by splňoval tento typ:

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

Scénář 2

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

To není cyklický odkaz a mohou být spokojeni jako tento:

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

Scénář 3

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

To dává smysl, aby mi, že to nebude fungovat. Stejně jako ve Scénáři 1, tam by byl žádný způsob, jak vytvořit instanci, která splňuje tuto podmínku.

Scénář 4

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

To není cyklický odkaz a mohou být spokojeni jako tento:

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

Scénář 5

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

To dává smysl, aby mi, že to nebude fungovat. Stejně jako ve Scénáři 1, tam by byl žádný způsob, jak vytvořit instanci, která splňuje tuto podmínku.

Scénář 6

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

Vlastně můžu napsat funkci pro splnění tohoto omezení, ale nejsem si jistý, co to je, jak mě dostat:

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

V jiných jazycích

Tato stránka je v jiných jazycích

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