简体   繁体   English

Typescript:按接口过滤 Object 或 Class

[英]Typescript: Filter Object by Interface or Class

I would like to sync an object that is provided by an API to a table.我想将 API 提供的 object 同步到表中。 The table is defined in Sequelize and has an interface and a class:该表在 Sequelize 中定义,具有接口和 class:

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

The response of the API is not always the same, but could look something like this: API 的响应并不总是相同,但可能如下所示:

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

Now, I would only like create a new object that can be passed to sequelize that has the matching contents, for example:现在,我只想创建一个新的 object 可以传递给具有匹配内容的 sequelize,例如:

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

How can I match the object properties keys to the interface or class?如何将 object 属性键与接口或 class 匹配?

Thanks!谢谢!

I think you need to extend your TypeScript with custom transformer like this one: ts-transformer-keys to be able to get the keys of an interface and the filter your response by copying only those keys.我认为您需要使用像这样的自定义转换器扩展您的 TypeScript: ts-transformer-keys以便能够获取接口的键并通过仅复制这些键来过滤您的响应。

Plain TypeScript doesn't generally allow pure type information to be passed to runtime code.普通的 TypeScript 通常不允许将纯类型信息传递给运行时代码。 So you can't see the interface at runtime and you can't know what are its fields to filter them.所以你在运行时看不到界面,也不知道它的哪些字段可以过滤它们。

You could get property names of a class, but you have to set them in your code:您可以获得 class 的属性名称,但您必须在代码中设置它们:

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
}

Alternatively you could declare interface in this weird roundabout way through enum because enum is one of the TypeScript construct that are accessible at runtime:或者,您可以通过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
}

If I understand you right, you:如果我理解正确,您:

Have: API which produces not 100% predictable response.有: API 产生非 100% 可预测的响应。

Want: to create a concrete instance of the class from this untrusted source想要:从这个不受信任的来源创建 class 的具体实例

If I am right you have two options:如果我是对的,你有两个选择:

If input object is not very big and dynamic you could do everything explicitly:如果输入 object 不是很大且动态的,您可以明确地执行所有操作:

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

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

This code is more-less OK except it is toooo verbose.除了冗长之外,这段代码还可以。

As a bit more advanced solution, you can create TransformationMapper, something like this:作为更高级的解决方案,您可以创建 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);

UPDATE更新

Is there any way, I could the get keyof MyClass programmatically有什么办法,我可以通过编程方式获取 MyClass 的 keyof

The main idea is to get a scheme to parse the original response.主要思想是获得一个解析原始响应的方案。 Luckily, you already got it.幸运的是,你已经明白了。

So you need to: create an instance of the desired class, and get keys from it:因此,您需要:创建所需 class 的实例,并从中获取密钥:

This can be done using:这可以使用:

Object.keys() Object.keys()

Object.entries() 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)

But I also have to warn you.但我也必须警告你。 If you don't trust API, you should not only parse, but also validate values you are parsing.如果您不信任 API,您不仅应该解析,还应该验证正在解析的值。 In another case, incorrect types provided via API may break your app.在另一种情况下,通过 API 提供的错误类型可能会破坏您的应用程序。

You can write your own validation (it is not that hard) or take something existing like yup您可以编写自己的验证(这并不难)或采用现有的东西,例如是的

You can get fields of class programmatically if you set default values to them and instantiate the class如果您为其设置默认值并实例化 class,您可以通过编程方式获取 class 的字段

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM