Hvordan kan jeg selektivt kopiere egenskaper fra ett objekt til et annet i TypeScript


Jeg prøver å selektivt kopiere nøkkel-verdi-par fra ett objekt til et annet, slik at jeg kan fjerne egenskaper fra et objekt selektivt, på denne måten:

sanitizeBody: function (obj: object, ...allowed: string[]) {
    const sanitizedObject = {};
    Object.keys(obj).forEach((el: string) => {
      if (allowed.includes(el)) {
        sanitizedObject[el] = obj[el];
      }
    });
  }

Basert på, poenget med funksjonen er at den tar et objekt, det vil si req.body, og en liste med strenger, og utfører denne operasjonen. For eksempel kan en eksempelinput se slik ut: sanitizeBody(req.body, 'role', 'active', 'status').

Dette fungerte i Javascript, men i TypeScript får jeg stadig feilen:

Element implicitly has an 'any' type because expression of type 'string' can't be used to index type '{}'.
  No index signature with a parameter of type 'string' was found on type '{}'.

Jeg er ikke sikker på hvordan jeg skal fikse dette.

Svar #1

Her er en løsning som implisitt velger nøklene du oppgir på riktig måte og opprettholder typene med utdataene.

// We have to make sure that the object you provided extends a record whose keys
// are of type string, im pretty sure there is a better way to do that but the
// use of any sufficient.
function sanitize<T extends Record<string, any>, K extends keyof T>(obj: T, ...keys: Array<K>): Pick<T, K> {

  // we then apply your desired code as provided, its still the same as how you did
  // it, just a different approach
  return Object.entries(obj).reduce((a, [k, v]) => {

    // I had to cheat and use find because the includes function only accepts
    // string values but the way the argument is layed out makes it possible for
    // K to be boolean
    // if (keys.includes(k)) { type error here
    if (keys.find((a) => a === k)) {
      a[k as K] = v;
    }
    return a;
  }, {} as Pick<T, K>);
}

// So when we test it here we should get the desired output.
const test = {
  a: 1,
  b: 4,
  c: 6
};

const output_a = sanitize(test, 'a', 'b');

// type error here
output_a.c;
// no type error here
output_a.a;

console.log(output_a);

Merk at selv om det finnes generiske typer definert i funksjonen, trenger du ikke å spesifisere dem når du kaller den fordi argumentene dine antyder hvilken generisk type som brukes. Derfor er de generiske typene implisitte.

Her er en lenke til lekeplassen min

Svar #2

Indekssignatur i TypeScript

Typescript sier at når vi ikke kjenner navnet på egenskapene, men vi kjenner formen på verdiene, kan vi i så fall gi en indeksignatur for å beskrive disse verdiene sammen med typen til indeksen.

I ditt tilfelle forstår ikke TypeScript indeks-signaturen til typen object, så du må eksplisitt definere indeks-signaturen for objektet ditt. Nedenfor er den idiomatiske måten å definere det på.

type IndexSignature = {
  [key: string]: string; //in your case value is string
}

Nå må du gjøre noen endringer siden vi har IndexSignature.

sanitize function(obj: IndexSignature, ...allowed: string[]) {
    const sanitizedObject = {} as IndexSignature;
    Object.keys(obj).forEach((el: string) => {
      if (allowed.includes(el)) {
        sanitizedObject[el] = obj[el];
      }
    });
  }

Relaterte artikler