Căutare TypeScript din literal de obiect


Am un literal de obiect astfel în Typescript:

const data={
  a:'valueOfA',
  b:'valueOfB,
  ...
}

Atunci trebuie să actualizez valorile în diferite locuri trecând obiectul și proprietatea de actualizat astfel:

function update(obj,prop){
...
}
update(data,'a');

Acest lucru funcționează, desigur, dar mi-ar plăcea să accesez proprietatea folosind notația cu punct și să profit de intellisense în IDE. Astfel, pot apela funcția în felul următor:

update(data,KeysOfData.a);

Am realizat o primă implementare care funcționează bine și oferă intellisense, dar mi se pare destul de urâtă și aș vrea să știu dacă există modalități mai bune de a obține rezultatul. Iată implementarea actuală:

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);

Răspuns #1

Voi trata această întrebare ca pe una care întreabă cum să folosești genericele și keyof pentru a-ți face codul reutilizabil și a îmbunătăți siguranța tipurilor.


Prima sugestie: Soluția ta, dar folosind generice

Poți extrage implementarea ta astfel încât să o poți reutiliza pentru alte obiecte astfel:

// 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);

Totuși, ceea ce spune acest tip este că o proprietate a lui T este de asemenea o proprietate a obiectului de căutare care are ca valoare un anumit șir, un tip mai corect ar fi:

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>)
}

Acum, când vei trece cursorul peste Fields.firstName, vei vedea și valoarea exactă a tipului. Totuși, va apărea că undefined este o posibilitate. Asta pentru că nicăieri în tip nu se specifică că toate câmpurile sunt incluse în căutare.

Dacă acest lucru cauzează o problemă pentru tine oriunde în cod, pur și simplu fă Fields.firstName!


A doua sugestie: Tastează update

Dacă poți renunța la abilitatea IDE-ului de a-ți oferi sugestii când dai clic pe punct și vrei doar siguranță tip și sugestii prin trecerea cursorului peste funcție pentru a obține tipul dedus, poți să renunți la întreaga implementare și doar să scrii update astfel:

function update<T>(obj: T, prop: keyof T): void


// Later call to update
update(formData, 'firstName');

Rețineți că această sugestie nu exclude pe cea anterioară, puteți folosi ambele.

Dacă faci asta, va trebui să folosești versiunea mai avansată a implementării din prima sugestie, cu semnul exclamării, adică să faci:

// 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!);