Typescript: 通过接口或类过滤对象

4

我希望将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: "..."
}

现在,我只想创建一个新对象,该对象可以传递给sequelize,并具有匹配的内容,例如:
const filteredResponse = {
  value2: "..."
  value3: 0
}

我该如何将对象属性的键与接口或类匹配?

谢谢!

3个回答

2

如果我理解正确,您:

有:一个API,其响应不是100%可预测的。

想要:从这个不受信任的来源创建一个类的具体实例。

如果我没错,您有两个选择:

如果输入对象不是非常大且动态的,您可以明确地完成所有操作:

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

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

这段代码基本上还可以,只是太啰嗦了。

作为更高级的解决方案,你可以创建一个 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提供的不正确的类型可能会破坏你的应用程序。

你可以编写自己的验证(这并不难),或者使用像yup这样的现有工具。


嗨@Drag13,非常感谢你的回答!响应可能相当大,这就是为什么您的lower方法会更好。我刚试过了,它似乎运行得很好。 有没有办法在程序中获取MyClass的keyof,而不是填充数组? - lucbas
@lucbas 你好,抱歉晚回复了,我周末有事。当然,这可以完成,我将在一个小时内更新帖子。 - Drag13

2
我认为你需要使用类似于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 的方式来声明接口,因为枚举是 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
}

2

如果您设置默认值并实例化类,则可以通过编程方式获取类的字段

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接