Je možné odvodiť kľúče záznamu (Record) v TypeScripte?
Používam JSS a chcem definovať objekt style s kľúčmi a hodnotami s pevne stanoveným typom, bez toho aby som musel kľúče definovať dvakrát.
Prvý pokus:
const style: Record<string, CSSProperties> = {
root: {
background: 'red'
},
title: {
fontWeight: 'bold'
},
...
}
Teraz style nie je silne typovaný, takže kompilátor nevydáva žiadne varovanie pri prístupe k style.nonExistingKey.
Druhý pokus:
Ak zadám kľúče explicitne takto:
const style: Record<'root' | 'title' | ... , CSSProperties> = {
root: {
background: 'red'
},
...
}
potom dostanem silne typovaný rekord, t. j. style.nonExistingKey vyhodí chybu. Táto metóda však vyžaduje zdvojovanie kľúčov záznamu, pretože musia byť explicitne pridané ako generický argument.
Tretí pokus:
Môžem vytvoriť silne typovaný záznam aj po fakte pomocou nasledujúceho kódu:
const styleObj = {
root: {
background: 'red'
},
title: {
fontWeight: 'bold'
},
...
}
const style = styleObj as Record<keyof typeof styleObj, CSSProperties>
Avšak potom stratím kontrolu typov pre hodnoty CSSProperties v zázname, takže toto nie je dobré riešenie.
Existuje spôsob, ako urobiť niečo takéto:
const style: Record<T, CssProperties> = {
root: {
background: 'red'
},
...
}
a mať T automaticky odvodzené ako 'root' | 'title' | ... atď.?
Odpoveď č. 1
S TypeScript 4.9 si myslím, že toto je konečne možné dosiahnuť použitím nového operátora satisfies:
const style = {
root: {
background: 'red'
},
title: {
fontWeight: 'bold'
},
invalid: {
invalidProperty: '' // Type '{ invalidProperty: string; }' is not assignable to type 'CSSProperties'.
// Object literal may only specify known properties, and 'invalidProperty' does not exist in type 'CSSProperties'.(2322)
}
} satisfies Record<string, CSSProperties>;
type keys = keyof typeof style; // "root" | "title" | "invalid"
style.nonExistingKey; // Property 'nonExistingKey' does not exist on type '{ ... }'.(2339)
style.root.fontWeight; // Property 'fontWeight' does not exist on type '{ background: string; }'.(2339)
detské ihrisko
Ako je uvedené v príkladoch vyššie, toto zachováva silné typovanie všetkých kľúčov a hodnôt v objekte a zabraňuje pridávaniu vlastností, ktoré nespĺňajú typovanie.
Odpoveď č. 2
Môžete použiť pomocnú funkciu pri definovaní objektu. Funkcia bude mať typové parametre, ktoré vyžadujú, aby všetky vlastnosti boli typu CSSProperties s použitím indexového podpisu. Keďže je funkcia generická, výsledok bude správne typovaný (skutočne nebude mať indexový podpis, ale iba definované vlastnosti)
function createStyleMap<T extends { [name: string]: CSSProperties }>(cfg: T) {
return cfg;
}
const style = createStyleMap({
root: {
background: 'red'
},
title: {
fontWeight: 'bold'
}
});
style.root //ok
style['hu'] // error
Môžete to tiež napísať tak, aby sa vrátil Record, ale nemyslím si, že by to prinieslo nejakú hodnotu
function createStyleMap<T extends { [name: string]: CSSProperties }>(cfg: T) : Record<keyof T, CSSProperties> {
return cfg;
}
