Ver microsoft/Transcripción#41164 para un canónica respuesta a esta pregunta.
Manuscrito no permitir que las referencias circulares en genérico interfaces y genérico de las clases, ya que las interfaces y las instancias de una clase estáticamente conocido propiedad/miembro/método de la teclas y para cualquier circularidad que ocurre en la "caja fuerte" lugares como la propiedad valor des o parámetros de método o tipo de retorno.
interface Interface<T> { val: T }
type X = Interface<X> // okay
class Class<T> { method(arg: T): void { } }
type Y = Class<Y> // okay
Pero para los alias de tipo no existe tal garantía. Tipo de alias puede tener cualquier estructura que cualquier tipo anónimo puede tener, por lo que el potencial de la circularidad no está limitado a recursiva de árbol de objetos:
type Safe<T> = { val: T };
type Unsafe<T> = T | { val: string };
Cuando el compilador crea una instancia de un tipo genérico, se aplaza su evaluación; no tratar de inmediato totalmente calcular el tipo resultante. Todo lo que se ve es de la forma:
type WouldBeSafe = Safe<WouldBeSafe>;
type WouldBeUnsafe = Unsafe<WouldBeUnsafe>;
Ambos tienen el mismo aspecto que el compilador... type X = SomeGenericTypeAlias<X>
. No puede "ver" que WouldBeSafe
estaría bien:
//type WouldBeSafe = { val: WouldBeSafe }; // would be okay
mientras WouldBeUnsafe
sería un problema:
//type WouldBeUnsafe = WouldBeUnsafe | { val: string }; // would be error
Puesto que no puede ver la diferencia, y porque al menos algunos de los usos sería ilegal circular, sólo se prohíbe a todos ellos.
Así que, ¿qué puedes hacer? Este es uno de esos casos donde me gustaría sugerir el uso de interface
en lugar de type
cuando usted puede. Usted puede reescribir su record
(tipo de cambio a MyRecord
para la convención de nomenclatura razones) como un interface
y todo el trabajo:
interface MyRecord<T> { val: T };
type B = MyRecord<B>; // okay
Usted puede incluso reescribir su func
(tipo de cambio a Func
para la convención de nomenclatura razones de nuevo) como un interface
cambiando el tipo de función de expresión de sintaxis en una llamada sintaxis de firma:
interface Func<T> { (arg: T): void }
type C = Func<C>; // okay
Por supuesto, hay situaciones en las que usted no puede hacerlo directamente, tales como la incorporada en el Record
tipo de utilidad:
type Darn = Record<string, Darn>; // error
y no se puede reescribir la asignada tipo de Record
como un interface
. Y, de hecho, no sería seguro para intentar hacer que las teclas de circular, como type NoGood = Record<NoGood, string>
. Si sólo quieres hacer Record<string, T>
para genéricos T
usted puede reescribir que como un interface
:
interface Dictionary<T> extends Record<string, T> { };
type Works = Dictionary<Works>;
Así que hay muy a menudo una manera de utilizar un interface
en lugar de type
para permitir que usted para expresar "seguro" recursiva tipos.
Patio enlace a código