Viz microsoft/Strojopis#41164 pro kanonické odpověď na tuto otázku.
Strojopis má 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