Voir microsoft/Tapuscrit#41164 pour un canoniques réponse à cette question.
Tapuscrit ne permettent références circulaires dans les génériques des interfaces et des génériques de classes, depuis les interfaces et les instances de classe ont connu statiquement propriété/membre/méthode clé des et ainsi, toute la circularité qui se passe dans "sécurité" des endroits comme la propriété de la valeurs ou des paramètres de la méthode ou le type de retour.
interface Interface<T> { val: T }
type X = Interface<X> // okay
class Class<T> { method(arg: T): void { } }
type Y = Class<Y> // okay
Mais pour le générique de type alias il n'y a aucune garantie. Type d'alias peut avoir n'importe quelle structure de tout type anonyme peut avoir, de sorte que le potentiel de la circularité n'est pas limité à récursive de l'arbre des objets similaires à:
type Safe<T> = { val: T };
type Unsafe<T> = T | { val: string };
Lorsque le compilateur instancie un type générique, il reporte son évaluation; elle n'a pas immédiatement essayer de bien calculer le type résultant. Tous voit il est de la forme:
type WouldBeSafe = Safe<WouldBeSafe>;
type WouldBeUnsafe = Unsafe<WouldBeUnsafe>;
Les deux se ressemblent pour le compilateur... type X = SomeGenericTypeAlias<X>
. Il ne peut pas "voir" que WouldBeSafe
serait bien:
//type WouldBeSafe = { val: WouldBeSafe }; // would be okay
alors que WouldBeUnsafe
serait un problème:
//type WouldBeUnsafe = WouldBeUnsafe | { val: string }; // would be error
Depuis il ne peut pas voir la différence, et parce qu'au moins certains usages seraient illégalement circulaire, il est juste interdit à tous d'entre eux.
Alors, que pouvez-vous faire? C'est l'un de ces cas où, je te suggère de l'aide interface
au lieu de type
quand vous le pouvez. Vous pouvez réécrire votre record
type (changement de MyRecord
pour la convention de nommage des raisons) comme un interface
et tout fonctionnera:
interface MyRecord<T> { val: T };
type B = MyRecord<B>; // okay
Vous pouvez même réécrire votre func
type (changement de Func
pour la convention de nommage des raisons encore) comme un interface
en changeant le type de fonction de l'expression de la syntaxe d'un appel de la signature de la syntaxe:
interface Func<T> { (arg: T): void }
type C = Func<C>; // okay
Bien sûr, il ya des situations où vous ne pouvez pas le faire directement, telles que le haut- Record
utilitaire de type:
type Darn = Record<string, Darn>; // error
et vous ne pouvez pas réécrire l' mappé type Record
comme un interface
. Et en effet, il serait dangereux d'essayer de rendre les clés de la circulaire, comme type NoGood = Record<NoGood, string>
. Si vous voulez seulement faire Record<string, T>
pour le générique T
vous pouvez réécrire que comme un interface
:
interface Dictionary<T> extends Record<string, T> { };
type Works = Dictionary<Works>;
Donc, il y a assez souvent une façon d'utiliser un interface
au lieu de type
pour vous permettre de vous exprimer "safe" récursive types.
Aire de jeux lien vers le code