typescript - De ce adăugarea parantezelor elimină 'readonly [x: number]: number;' din acest tip?


enum Animals {
  CAT,
  DOG,
  PARROT,
  SHEEP,
  SALMON
}

Următorul tip:

type AnimalsToMappedType = {
  [K in keyof typeof Animals]: K
}

Rezultă în:

type AnimalsToMappedType = {
  readonly [x: number]: number;
  readonly CAT: "CAT";
  readonly DOG: "DOG";
  readonly PARROT: "PARROT";
  readonly SHEEP: "SHEEP";
  readonly SALMON: "SALMON";
}

Dar dacă adaugi paranteze în jurul lui keyof typeof Animals:

type AnimalsToMappedType = {
  [K in (keyof typeof Animals)]: K
}

Rezultatul este:

type AnimalsToMappedType = {
    CAT: "CAT";
    DOG: "DOG";
    PARROT: "PARROT";
    SHEEP: "SHEEP";
    SALMON: "SALMON";
}

ÎNTREBARE

Ce fac parantezele care elimină readonly [x: number]: number;?

Răspuns #1

Vezi microsoft/TypeScript#40206 pentru un răspuns autoritar.

Un tip mapat de forma {[K in keyof XXX]: YYY} este considerat un tip mapat omomorf, unde TypeScript vede in keyof și recunoaște că transformi un alt tip XXX, și, prin urmare, poate folosi informații despre tipul original care nu sunt prezente în rezultatele keyof. Pe de altă parte, un tip mapat de forma similară {[K in (keyof XXX)]: YYY} nu este considerat omomorf. Parantezele determină TypeScript să calculeze mai întâi keyof XXX, iar apoi tipul mapat iterează peste aceste rezultate fără să știe sau să-și amintească ceva despre XXX. Astfel, in keyof XXX păstrează informații despre XXX pe care in (keyof XXX) le aruncă.

Această diferență este cel mai frecvent observată atunci când se transformă un tip cu proprietăți opționale și readonly. Un tip mapat omomorf va păstra opționalele/readonly, chiar dacă astfel de informații nu sunt vizibile prin keyof. De exemplu, dacă XXX este {a?: string, readonly b: number}, atunci {[K in keyof XXX]: YYY} va produce {a?: YYY, readonly b: YYY}, în timp ce {[K in (keyof XXX): YYY} va produce {a: YYY, b: YYY}.

În exemplul tău, operezi pe un obiect enum numeric, care are mapări inverse. Dacă Animals.CAT === 0 atunci Animals[0] === "CAT". TypeScript modelează acest lucru prin adăugarea unei semnături de index numeric la obiectul enum, dar această semnătură de index este suprimată atunci când folosești keyof. Deci, deși typeof Animals ar putea arăta ca { [k: number]: string; CAT: 0 }, keyof typeof Animals este doar "CAT" și nu number | "CAT". Deci un tip mapat homomorf peste un enum numeric va avea o semnătură de index numeric, în timp ce un tip mapat non-homomorf peste keyof al unui enum numeric nu va avea.