Búsqueda de sitios web

javascript - Typescript: Filtrar objeto por interfaz o clase


Me gustaría sincronizar un objeto que es proporcionado por una API con una tabla. La tabla está definida en Sequelize y tiene una interfaz y una clase:

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

La respuesta de la API no siempre es la misma, pero podría verse algo así:

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

Ahora, solo me gustaría crear un nuevo objeto que pueda ser pasado a Sequelize y que tenga el contenido correspondiente, por ejemplo:

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

¿Cómo puedo hacer que las claves de las propiedades de un objeto coincidan con la interfaz o clase?

¡Gracias!

Respuesta #1

Si te entiendo bien, tú:

Tener: API que produce una respuesta no 100% predecible.

Deseo: crear una instancia concreta de la clase a partir de esta fuente no confiable

Si tengo razón, tienes dos opciones:

Si el objeto de entrada no es muy grande y dinámico, podrías hacer todo explícitamente:

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

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

Este código está más o menos bien, excepto que es demasiado verboso.

Como una solución un poco más avanzada, puedes crear TransformationMapper, algo así:

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

ACTUALIZACIÓN

¿Hay alguna manera de obtener programáticamente la clave de MyClass?

La idea principal es obtener un esquema para analizar la respuesta original. Afortunadamente, ya lo tienes.

Entonces necesitas: crear una instancia de la clase deseada y obtener claves de ella:

Esto se puede hacer usando:

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)

Pero también tengo que advertirte. Si no confías en la API, no solo deberías analizar, sino también validar los valores que estás analizando. De lo contrario, los tipos incorrectos proporcionados a través de la API pueden romper tu aplicación.

Puedes escribir tu propia validación (no es tan difícil) o tomar algo existente como yup

Respuesta #2

Creo que necesitas ampliar tu TypeScript con un transformador personalizado como este: ts-transformer-keys para poder obtener las claves de una interfaz y filtrar tu respuesta copiando solo esas claves.

El TypeScript puro generalmente no permite que la información de tipo puro se pase al código en tiempo de ejecución. Por lo tanto, no puedes ver la interfaz en tiempo de ejecución y no puedes saber cuáles son sus campos para filtrarlos.

Podrías obtener los nombres de las propiedades de una clase, pero tienes que configurarlos en tu código:

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
}

Alternativamente, podrías declarar una interfaz de esta extraña manera indirecta a través de enum porque enum es uno de los constructos de TypeScript que son accesibles en tiempo de ejecución:

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
}

Artículos relacionados