Typescript-uppslag från objektsliteral
Jag har ett objekt literal som detta i Typescript:
const data={
a:'valueOfA',
b:'valueOfB,
...
}
Då behöver jag uppdatera värden på olika ställen genom att skicka objektet och egenskapen som ska uppdateras på detta sätt:
function update(obj,prop){
...
}
update(data,'a');
Detta fungerar förstås, men jag skulle vilja komma åt egenskapen med punktnotation och dra nytta av intellisense i IDE:n. Så jag kan kalla på funktionen så här:
update(data,KeysOfData.a);
Jag gjorde en första implementation som fungerar bra och ger intellisense, men den ser ganska ful ut för mig och jag skulle vilja veta om det finns bättre sätt att uppnå resultatet. Här är den nuvarande implementationen:
type FormProps = {
firstName?: string,
lastName?: string,
email?: string
}
const formData: FormProps = {
firstName: '',
lastName: '',
email: ''
}
type FormItemName = keyof FormProps;
type FormItem = {
[key in FormItemName]: string
}
// Generates lookup object
const Fields: FormItem = Object.keys(formData).reduce<FormItem>((obj, field) => {
obj[field as FormItemName] = field;
return obj;
}, {} as any);
// Later call to update
update(formData,Fields.firstName);
Svar #1
Jag kommer att behandla denna fråga som en som frågar hur man använder generics och keyof för att göra din kod återanvändbar och förbättra typ säkerheten.
Första förslaget: Din lösning, men med generics
Du kan extrahera din implementation så att du kan återanvända den för andra objekt på följande sätt:
// Generic reusable lookup type
type FieldLookup<T> = { [key in keyof T]: string }
// Generic reusable lookup generation function
function generateFieldLookup<T>(data: T): FieldLookup<T> {
return Object.keys(formData).reduce((obj, field) => {
obj[field as keyof T] = field;
return obj;
}, {} as FieldLookup<T>)
}
// Generates lookup object
const Fields = generateFieldLookup(formData)
// Later call to update
update(formData, Fields.firstName);
Men vad denna typ säger är att en egenskap hos T också är en egenskap hos uppslagsobjektet som har någon sträng som sitt värde, en mer korrekt typ skulle vara:
type FieldLookup<T> = { [key in keyof T]: key; }
function generateFieldLookup<T>(data: T): FieldLookup<T> {
return Object.keys(formData).reduce((obj, field) => {
const key = field as keyof T;
obj[key] = key;
return obj;
}, {} as FieldLookup<T>)
}
Nu när du hovrar över Fields.firstName får du också dess exakta värde av typen. Däremot kommer det att stå att undefined är en möjlighet. Det beror på att det inte står någonstans i typen att alla fält ingår i uppslaget.
Om detta orsakar problem för dig någonstans i koden, gör bara Fields.firstName!
Andra förslaget: Skriv update
Om du kan offra möjligheten för IDE:n att ge dig förslag när du klickar på punkten, och bara vill ha typkontroll och förslag genom att hovra över funktionen för att få den härledda typen, kan du göra dig av med hela denna implementation och bara skriva update som:
function update<T>(obj: T, prop: keyof T): void
// Later call to update
update(formData, 'firstName');
Observera att detta förslag inte utesluter det föregående, du kan använda båda.
Om du gör det måste du använda den mer avancerade versionen av implementeringen i det första förslaget, med utropstecknet, dvs. gör:
// Generic reusable lookup type
type FieldLookup<T> = { [key in keyof T]: key }
function update<T>(obj: T, prop: keyof T): void
// Later call to update
update(formData, Fields.firstName!);
