Perché dattiloscritto non consente riferimenti circolari in generics?

0

Domanda

qui gli esempi in cui il tipo fa riferimento direttamente a se stessa, nella sua definizione, ma quando astratto via generica di un esito negativo.

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

Migliore risposta

3

Vedere microsoft/Dattiloscritto#41164 per una canonica risposta a questa domanda.

Dattiloscritto non consente riferimenti circolari in generico interfacce e generico classi, dal momento che le interfacce e le istanze di classe sono staticamente nota proprietà/membri/metodo tastos e quindi qualsiasi circolarità accade in "cassaforte" luoghi come la proprietà values o parametri di un metodo o tipo di ritorno.

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

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

Ma per generico tipo alias non c'è alcuna garanzia. Tipo alias può avere qualsiasi struttura che qualsiasi tipo anonimo, in modo che il potenziale di una circolarità non è vincolata a ricorsiva di struttura ad albero di oggetti:

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

Quando il compilatore crea un'istanza di un tipo generico, si posticipa la sua valutazione; non tentare subito completamente calcolare la risultante tipo. Tutti si vede è la forma:

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

Sia di quelli che lo stesso aspetto per il compilatore... type X = SomeGenericTypeAlias<X>. E non può "vedere" che WouldBeSafe non sarebbe male:

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

mentre WouldBeUnsafe sarebbe un problema:

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

Poiché non può vedere la differenza, e perché almeno alcuni usi sarebbe illegalmente circolare, solo che li proibisce tutti.


Quindi, cosa si può fare? Questo è uno di quei casi in cui suggerirei interface invece di type quando è possibile. Puoi riscrivere il tuo record tipo (cambiando di MyRecord per la convenzione di denominazione motivi) come interface e tutto funzionerà:

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

Si può anche riscrivere il tuo func tipo (cambiando di Func per la convenzione di denominazione motivi, ancora una interface cambiando il tipo di funzione di espressione di sintassi in un call firma sintassi:

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

Ovviamente, ci sono situazioni in cui non si può fare direttamente, come il built-in Record tipo di utilità:

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

e non è possibile riscrivere la mapped tipo Record come interface. E, infatti, sarebbe pericoloso per cercare di rendere le chiavi circolare, come type NoGood = Record<NoGood, string>. Se si desidera solo per fare Record<string, T> per generico Tsi può riscrivere che come un interface:

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

Quindi c'è molto spesso un modo per utilizzare un interface invece di type per consentire di esprimere "sicuro" ricorsiva tipi.

Giochi link al codice

2021-11-23 21:31:48

grazie! questo è fresco e disponibile!
radiish
1

Analizziamo questi scenari, uno per uno.

Scenario 1

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

È interessante, questo è permesso. Non vedo come si potrebbe essere in grado di costruire un'istanza per soddisfare questo tipo:

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

Scenario 2

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

Questo non è un riferimento circolare e può essere soddisfatto come questo:

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

Scenario 3

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

Ha senso per me che questo non funziona. Proprio come nello Scenario 1, non ci sarebbe alcun modo per costruire un'istanza che soddisfa questo vincolo.

Scenario 4

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

Questo non è un riferimento circolare e può essere soddisfatto come questo:

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

Scenario 5

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

Ha senso per me che questo non funziona. Proprio come nello Scenario 1, non ci sarebbe alcun modo per costruire un'istanza che soddisfa questo vincolo.

Scenario 6

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

Posso scrivere una funzione per soddisfare questa limitazione, ma io non sono sicuro di quello che è sempre con me:

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

In altre lingue

Questa pagina è in altre lingue

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