Typescript-funktion som tar emot en array av strängar och returnerar ett objekt med givna strängar som nycklar


Hur skulle man korrekt typescripta följande metod:

const getVars = (vars: string[]) => {
  const data = [
    {
      "someId": "28",
      "someAttr": "2",
    },
    {
      "someId": "43",
      "someOtherAttr": "3",
    },
    {
      "someOtherId": "32",
      "someAttr": "4",
    }
  ] // an array containing some data

  const result = vars.reduce((acc, _var) => ({
    ...acc,
    ...data.reduce((acc, acc) => ({
      ...acc,
      [...acc[_var], curr[_var]]
    }, {})
  }), {})

  return result
}

användning :

const { someId, someAttr } = getVars(['someId', 'someAttr'])

output: someId: ['28', '43'], someAttr: ['2', '4']

Jag skulle vilja typescripta getVars så att returtypen speglar givna parametrar, till exempel, om jag skickar ['someId'] som parameter, skulle typescript veta att funktionen returnerar ett objekt med nyckeln 'someId'.

Svar #1

Du kan skriva detta på ett slags-sätt strikt eller mycket strikt. Det beror på hur mycket information du vet från början (vet du nycklarna för elementen i data?) och hur galen du vill vara. Din data här är en array av objects med string-nycklar. Är värdena alltid string? Eller råkar de bara vara det i detta exempel?

För den typ av halv-strikta tillvägagångssättet antar jag att värdena alltid är string och att data har typen Record<string, string>[].

Vi vill veta de specifika strängvärden som vi skickade till getVars, så vi kommer att använda en generisk T extends string. Vi returnerar ett objekt som har dessa specifika strängar T som nycklar och värdena är en array av strängar. Så returtypen är Record<T, string[]>.

När du bygger ett objekt genom reduce() behöver du använda as för att fastställa typen på det slutliga objektet.

const getVars = <T extends string>(vars: T[]): Record<T, string[]> => {
    return vars.reduce((acc, _var) => ({
        ...acc,
        [_var]: data.map(o => o[_var]).filter(v => v !== undefined)
    }), {} as Record<T, string[]>);
}

const { someId, someAttr } = getVars(['someId', 'someAttr'])
console.log(someId, someAttr);

Typescript Playground-länk

Svar #2

const data = [
    {
      "someId": "28",
      "someAttr": "2",
    },
    {
      "someId": "43",
      "someOtherAttr": "3",
    },
    {
      "someOtherId": "32",
      "someAttr": "4",
    }
  ];

const getVars = (data, vars) => {
  return data.reduce((acc, curr) => {
    Object.keys(curr).filter(key => vars.includes(key)).forEach(
      key => acc[key] = [...(acc[key] || []), curr[key]],
    );
    
    return acc;
  }, {});
}

console.log(getVars(data, ['someId', 'someAttr']));

Relaterade artiklar