typescript - Ce este tipul Record?
Ce înseamnă Record<K, T> în Typescript?
Typescript 2.1 a introdus tipul Record, descriindu-l într-un exemplu:
// For every properties K of type T, transform it to U function mapObject<K extends string, T, U>(obj: Record<K, T>, f: (x: T) => U): Record<K, U>
vezi Typescript 2.1
Iar pagina Despre Tipuri Avansate menționează Record sub titlul Tipuri Mapate împreună cu Readonly, Partial și Pick, în ceea ce pare a fi definiția sa:
type Record<K extends string, T> = { [P in K]: T; }
Readonly, Partial și Pick sunt omomorfice, în timp ce Record nu este. Un indiciu că Record nu este omomorfic este că nu ia un tip de intrare pentru a copia proprietăți:
type ThreeStringProps = Record<'prop1' | 'prop2' | 'prop3', string>
Și asta este. În afară de citatele de mai sus, nu există nicio altă mențiune a Record pe typescriptlang.org.
Întrebări
Poate cineva să dea o definiție simplă a ceea ce este
Record?Record<K,T>este pur și simplu un mod de a spune „toate proprietățile acestui obiect vor avea tipulT”? Probabil nu toate proprietățile, deoareceKare un anumit scop...Interzice genericul
Kcheile suplimentare pe obiect care nu suntK, sau le permite și doar indică faptul că proprietățile lor nu sunt transformate înT?Cu exemplul dat:
type ThreeStringProps = Record<'prop1' | 'prop2' | 'prop3', string>Este exact la fel ca acesta?
type ThreeStringProps = {prop1: string, prop2: string, prop3: string}
Răspuns #1
- Poate cineva să dea o definiție simplă a ceea ce este
Record?
Un Record<K, T> este un tip de obiect ale cărui chei de proprietate sunt K și ale căror valori de proprietate sunt T. Adică, keyof Record<K, T> este echivalent cu K, iar Record<K, T>[K] este (practic) echivalent cu T.
- Este
Record<K,T>doar o modalitate de a spune „toate proprietățile acestui obiect vor avea tipulT”? Probabil nu toate obiectele, deoareceKare un anumit scop...
După cum observați, K are un scop... de a limita cheile proprietăților la valori anumite. Dacă doriți să acceptați toate cheile posibile cu valori de tip șir, ați putea face ceva de genul Record<string, T>, dar modul idiomatic de a face acest lucru este să folosiți o semnătură de index precum { [k: string]: T }.
- Interzice genericul
Kcheile suplimentare pe obiect care nu suntK, sau le permite și doar indică faptul că proprietățile lor nu sunt transformate înT?
Nu „interzice” exact chei suplimentare: la urma urmei, într-o valoare este în general permis să existe proprietăți care nu sunt menționate explicit în tipul său... dar nu ar recunoaște că astfel de proprietăți există:
declare const x: Record<"a", string>;
x.b; // error, Property 'b' does not exist on type 'Record<"a", string>'
și le-ar trata ca proprietăți excedentare care sunt uneori respinse:
declare function acceptR(x: Record<"a", string>): void;
acceptR({a: "hey", b: "you"}); // error, Object literal may only specify known properties
și uneori acceptat:
const y = {a: "hey", b: "you"};
acceptR(y); // okay
Cu exemplul dat:
type ThreeStringProps = Record<'prop1' | 'prop2' | 'prop3', string>Este exact la fel ca acesta?
type ThreeStringProps = {prop1: string, prop2: string, prop3: string}
Da!
Răspunsul #2
Un Record îți permite să creezi un nou tip dintr-un Union. Valorile din Union sunt folosite ca atribute ale noului tip.
De exemplu, să spunem că am o Uniune astfel:
type CatNames = "miffy" | "boris" | "mordred";
Acum vreau să creez un obiect care conține informații despre toate pisicile, pot crea un nou tip folosind valorile din uniunea CatNames ca chei.
type CatList = Record<CatNames, {age: number}>
Dacă vreau să satisfac acest CatList, trebuie să creez un obiect astfel:
const cats: CatList = {
miffy: { age:99 },
boris: { age:16 },
mordred: { age:600 }
}
Obții o siguranță de tip foarte puternică:
- Dacă uit o pisică, primesc o eroare.
- Dacă adaug o pisică care nu este permisă, primesc o eroare.
- Dacă mai târziu schimb
CatNames, primesc o eroare. Acest lucru este deosebit de util deoareceCatNameseste probabil importat dintr-un alt fișier și este probabil folosit în multe locuri.
Exemplu real de React.
Am folosit acest lucru recent pentru a crea un component Status. Componenta ar primi un prop status și apoi ar afișa o pictogramă. Am simplificat foarte mult codul aici în scop ilustrativ
Am avut o uniune de genul acesta:
type Statuses = "failed" | "complete";
Am folosit asta pentru a crea un obiect ca acesta:
const icons: Record<
Statuses,
{ iconType: IconTypes; iconColor: IconColors }
> = {
failed: {
iconType: "warning",
iconColor: "red"
},
complete: {
iconType: "check",
iconColor: "green"
};
Aș putea apoi să redau prin destructurarea unui element din obiect în props, astfel:
const Status = ({status}) => <Icon {...icons[status]} />
Dacă uniunea Statuses este extinsă sau modificată ulterior, știu că componenta mea Status nu se va compila și voi primi o eroare pe care o pot remedia imediat. Acest lucru îmi permite să adaug stări suplimentare de eroare în aplicație.
Rețineți că aplicația reală avea zeci de stări de eroare care erau menționate în mai multe locuri, așa că această siguranță a tipurilor a fost extrem de utilă.
Documente
Minunat să văd că mica mea și neînsemnata exemplu cu pisica face acum parte din documentația oficială TypeScript!
https://www.typescriptlang.org/docs/handbook/utility-types.html#recordkeys-type
Răspunsul #3
Există acum o documentație puțin mai lungă despre tipul Record: https://www.typescriptlang.org/docs/handbook/utility-types.html#recordkeys-type
Citat că:
Record<Chei, Tip> Lansat: 2.1
Construiește un tip de obiect ale cărui chei de proprietate sunt Keys și ale căror valori de proprietate sunt Type. Acest utilitar poate fi folosit pentru a mapa proprietățile unui tip către un alt tip.
interface CatInfo {
age: number;
breed: string;
}
type CatName = "miffy" | "boris" | "mordred";
const cats: Record<CatName, CatInfo> = {
miffy: { age: 10, breed: "Persian" },
boris: { age: 5, breed: "Maine Coon" },
mordred: { age: 16, breed: "British Shorthair" },
};
cats.boris;
const cats: Record<CatName, CatInfo>
