typescript - Hva er Record-typen?
Hva betyr Record<K, T> i Typescript?
Typescript 2.1 introduserte Record-typen, og beskrev den i et eksempel:
// 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>
se Typescript 2.1
Og siden Avanserte typer nevner Record under overskriften Mapped Types sammen med Readonly, Partial og Pick, i det som ser ut til å være dens definisjon:
type Record<K extends string, T> = { [P in K]: T; }
Readonly, Partial og Pick er homomorfe, mens Record ikke er det. Et tegn på at Record ikke er homomorf, er at det ikke tar en inputtype for å kopiere egenskaper fra:
type ThreeStringProps = Record<'prop1' | 'prop2' | 'prop3', string>
Og det er det. Bortsett fra sitatene ovenfor, er det ingen annen omtale av Record på typescriptlang.org.
Spørsmål
Kan noen gi en enkel definisjon på hva
Recorder?Er
Record<K,T>bare en måte å si «alle egenskaper på dette objektet vil ha typenT»? Sannsynligvis ikke alle egenskaper, sidenKhar et eller annet formål...Forbyr den generiske
Kekstra nøkler på objektet som ikke erK, eller tillater den dem og indikerer bare at deres egenskaper ikke blir transformert tilT?Med det gitte eksempelet:
type ThreeStringProps = Record<'prop1' | 'prop2' | 'prop3', string>Er det helt det samme som dette?
type ThreeStringProps = {prop1: string, prop2: string, prop3: string}
Svar #1
- Kan noen gi en enkel definisjon på hva
Recorder?
En Record<K, T> er en objekttype hvis egenskapsnøkler er K og hvis egenskapsverdier er T. Det vil si at keyof Record<K, T> er ekvivalent med K, og Record<K, T>[K] er (i hovedsak) ekvivalent med T.
- Er
Record<K,T>bare en måte å si "alle egenskaper på dette objektet vil ha typenT"? Sannsynligvis ikke alle objekter, sidenKhar en viss hensikt...
Som du påpeker, K har et formål... å begrense egenskapstastene til bestemte verdier. Hvis du vil akseptere alle mulige strengeverdiefelter, kan du gjøre noe som Record<string, T>, men den idiomatiske måten å gjøre det på er å bruke en indekssignatur som { [k: string]: T }.
- Forbyr den generiske
Kekstra nøkler på objektet som ikke erK, eller tillater den dem og indikerer bare at deres egenskaper ikke blir transformert tilT?
Det «forbyr» ikke akkurat ekstra nøkler: tross alt er det generelt tillatt for en verdi å ha egenskaper som ikke er eksplisitt nevnt i typen dens... men den ville ikke gjenkjenne at slike egenskaper eksisterer:
declare const x: Record<"a", string>;
x.b; // error, Property 'b' does not exist on type 'Record<"a", string>'
og det ville behandle dem som overflødige egenskaper som noen ganger blir avvist:
declare function acceptR(x: Record<"a", string>): void;
acceptR({a: "hey", b: "you"}); // error, Object literal may only specify known properties
og noen ganger akseptert:
const y = {a: "hey", b: "you"};
acceptR(y); // okay
Med det gitte eksempelet:
type ThreeStringProps = Record<'prop1' | 'prop2' | 'prop3', string>Er det helt det samme som dette?
type ThreeStringProps = {prop1: string, prop2: string, prop3: string}
Ja!
Svar #2
En Record lar deg opprette en ny type fra en Union. Verdiene i Union brukes som attributter for den nye typen.
For eksempel, si at jeg har en union som dette:
type CatNames = "miffy" | "boris" | "mordred";
Nå vil jeg lage et objekt som inneholder informasjon om alle kattene, jeg kan lage en ny type ved å bruke verdiene i CatNames-unionen som nøkler.
type CatList = Record<CatNames, {age: number}>
Hvis jeg vil tilfredsstille denne CatList, må jeg lage et objekt som dette:
const cats: CatList = {
miffy: { age:99 },
boris: { age:16 },
mordred: { age:600 }
}
Du får veldig sterk typesikkerhet:
- Hvis jeg glemmer en katt, får jeg en feil.
- Hvis jeg legger til en katt som ikke er tillatt, får jeg en feil.
- Hvis jeg senere endrer
CatNames, får jeg en feil. Dette er spesielt nyttig fordiCatNamessannsynligvis importeres fra en annen fil, og sannsynligvis brukes mange steder.
Ekte verdens React-eksempel.
Jeg brukte dette nylig for å lage en Status-komponent. Komponentet ville motta en status-prop, og deretter rendre et ikon. Jeg har forenklet koden ganske mye her for illustrasjonsformål
Jeg hadde en union som dette:
type Statuses = "failed" | "complete";
Jeg brukte dette for å lage et objekt som dette:
const icons: Record<
Statuses,
{ iconType: IconTypes; iconColor: IconColors }
> = {
failed: {
iconType: "warning",
iconColor: "red"
},
complete: {
iconType: "check",
iconColor: "green"
};
Jeg kunne da rendre ved å dekonstruere et element fra objektet inn i props, slik:
const Status = ({status}) => <Icon {...icons[status]} />
Hvis Statuses-unionen senere utvides eller endres, vet jeg at Status-komponenten min vil feile ved kompilering, og jeg vil få en feil som jeg kan fikse umiddelbart. Dette lar meg legge til flere feilsituasjoner i appen.
Merk at den faktiske appen hadde dusinvis av feilmeldinger som ble referert til mange steder, så denne typetil sikkerheten var ekstremt nyttig.
Dokumenter
Fantastisk å se at mitt dumme lille kateksempel nå er en del av de offisielle TypeScript-dokumentene!
https://www.typescriptlang.org/docs/handbook/utility-types.html#recordkeys-type
Svar #3
Det finnes nå en litt lengre dokumentasjon av Record-typen: https://www.typescriptlang.org/docs/handbook/utility-types.html#recordkeys-type
Siterer det:
Record<Keys, Type> Utgitt: 2.1
Konstruerer en objekttype hvis egenskapsnøkler er Keys og hvis egenskapsverdier er Type. Dette verktøyet kan brukes til å mappe egenskapene til en type til en annen type.
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>
