Машинопись: фильтр объекта по интерфейсу или классу - PullRequest
0 голосов
/ 29 мая 2020

Я хотел бы синхронизировать c объект, предоставляемый API, с таблицей. Таблица определена в Sequelize и имеет интерфейс и класс:

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

Ответ API не всегда одинаков, но может выглядеть примерно так:

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

Теперь я хотел бы создать только новый объект, который можно передать в сиквелиз, который имеет соответствующее содержимое, например:

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

Как мне сопоставить ключи свойств объекта с интерфейсом или классом ?

Спасибо!

Ответы [ 3 ]

1 голос
/ 29 мая 2020

Если я вас правильно понимаю, вы:

Имеете: API, который дает не 100% предсказуемый ответ.

Хотите: создать конкретный экземпляр класса из этого ненадежного источника

Если я прав, у вас есть два варианта:

Если входной объект не очень большой и динамический c, вы можете сделать все явно:

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

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

Этот код более-менее в порядке, за исключением того, что он toooo подробный.

В качестве немного более продвинутого решения вы можете создать TransformationMapper, примерно так:

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

ОБНОВЛЕНИЕ

Есть ли способ получить ключ MyClass программно

Основная идея состоит в том, чтобы получить схему для разбора исходного ответа. К счастью, вы его уже получили.

Итак, вам нужно: создать экземпляр нужного класса и получить от него ключи:

Это можно сделать с помощью:

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)

Но я также должен вас предупредить . Если вы не доверяете API, вам следует не только анализировать, но и проверять значения, которые вы анализируете. В другом случае неверные типы, предоставленные через API, могут сломать ваше приложение.

Вы можете написать свою собственную проверку (это не так сложно) или взять что-то существующее, например да

1 голос
/ 29 мая 2020

Вы можете получить поля класса программно, если вы установите для них значения по умолчанию и создадите экземпляр класса

class SampleClass {
    value1?= "";
    value2?= "";
    value3?= 0;

}
var keys = Object.keys(new SampleClass()) as (keyof SampleClass)[];

var ob = { value1: "asd", value2: "sdad", value4: "xxx" };

var result: SampleClass = {};
for (var k of keys) {
    if (k in ob) { // remove this line if you want set missing fields to undefined
        result[k] = (ob as any)[k];
    }
}

console.log(result);
1 голос
/ 29 мая 2020

Я думаю, вам нужно расширить свой TypeScript с помощью настраиваемого преобразователя вроде этого: ts-transformer-keys , чтобы иметь возможность получать ключи интерфейса и фильтровать свой ответ, копируя только эти ключи.

Обычный TypeScript обычно не позволяет передавать чистую информацию о типе во время выполнения. Таким образом, вы не можете видеть интерфейс во время выполнения и не можете знать, какие у него поля для их фильтрации.

Вы можете получить имена свойств класса, но вы должны установить их в своем коде:

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
}

В качестве альтернативы вы можете объявить интерфейс этим странным окольным путем через enum, потому что enum является одной из конструкций TypeScript, доступных во время выполнения:

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
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...