javascript - Typescript: Filtrer objekt etter grensesnitt eller klasse


Jeg vil gjerne synkronisere et objekt som leveres av et API til en tabell. Tabellen er definert i Sequelize og har et grensesnitt og en klasse:

declare interface SampleInterface {
  value1?: string;
  value2?: string;
  value3?: number;
}
class SampleClass implements SampleInterface {
  value1?: string;
  value2?: string;
  value3?: number;
}

Responsen fra API-et er ikke alltid den samme, men kan se noe slik ut:

const sampleResponse = {
  value2: "..."
  value3: 0
  value4: "..."
}

Nå vil jeg bare lage et nytt objekt som kan sendes til Sequelize som har de matchende innholdene, for eksempel:

const filteredResponse = {
  value2: "..."
  value3: 0
}

Hvordan kan jeg matche objektets egenskapsnøkler med grensesnittet eller klassen?

Takk!

Svar #1

Hvis jeg forstår deg riktig, du:

Har: API som produserer ikke 100% forutsigbart svar.

Ønsker: å lage en konkret instans av klassen fra denne utrygge kilden

Hvis jeg har rett, har du to alternativer:

Hvis inndataobjektet ikke er veldig stort og dynamisk, kunne du gjøre alt eksplisitt:

const unreliableObject = fetchFromApi();
const result = new Result();

if (typeof unreliableObject.name  === 'string') {
    result.name = unreliableObject.name;
}

Denne koden er mer eller mindre OK bortsett fra at den er altfor detaljert.

Som en litt mer avansert løsning, kan du lage TransformationMapper, noe slikt som dette:

class MyClass {
    name: string;
}

const expectedKeys: (keyof MyClass)[] = ['name'];
const data: any = { v: 1, name: '13212' };

const res = expectedKeys.reduce((result, fieldName) => {

    const value = data[fieldName];
    if (value != null) {
        result[fieldName] = data[fieldName]
    }

    return result;
}, new MyClass());

console.log(res);

OPPDATERING

Finnes det noen måte jeg programmessig kan få keyof MyClass på

Hovedideen er å få et skjema for å analysere det opprinnelige svaret. Heldigvis har du allerede fått det.

Så du trenger å: opprette en instans av ønsket klasse, og hente nøkler fra den:

Dette kan gjøres ved å bruke:

Object.keys()

Object.entries()

const data: any = {};

let result = new MyClass();
result = Object.keys(result).reduce((result, fieldName) => {

    const value = data[fieldName];
    if (value != null) {
        result[fieldName] = data[fieldName]
    }

    return result;
}, result)

Men jeg må også advare deg. Hvis du ikke stoler på API-et, bør du ikke bare analysere, men også validere verdier du analyserer. I motsatt tilfelle kan feil typer som gis via API bryte appen din.

Du kan skrive din egen validering (det er ikke så vanskelig) eller bruke noe eksisterende som yup

Svar #2

Jeg tror du trenger å utvide TypeScript med en tilpasset transformer som denne: ts-transformer-keys for å kunne få nøklene til et grensesnitt og filtrere svaret ditt ved å kopiere bare disse nøklene.

Ren TypeScript tillater generelt ikke at ren typeinformasjon sendes til kjøretidskode. Så du kan ikke se grensesnittet ved kjøretid, og du kan ikke vite hvilke felt det har for å filtrere dem.

Du kan få eiendomsnavnene til en klasse, men du må sette dem i koden din:

class SampleClass implements SampleInterface {
  value1?: string = "";
  value2?: string = "";
  value3?: number = 0;
}

var a = new SampleClass();
for (var k in a) {
    console.log(k);  // prints value1, value2, value3
}

Alternativt kunne du erklære et grensesnitt på denne merkelige omveien gjennom enum fordi enum er en av TypeScript-konstruksjonene som er tilgjengelige ved kjøretid:

enum Props {
    value1 = "value1",
    value2 = "value2",
    value3 = "value3"
};

declare interface SampleInterface {
  [Props.value1]?: string;
  [Props.value2]?: string;
  [Props.value3]?: number;
}

class SampleClass implements SampleInterface {
  value1?: string = "";
  value2?: string = "";
  value3?: number = 0;
}

for (var k in Props) {
    console.log(k);  // prints value1, value2, value3
}

Relaterte artikler