优化 JSON 到 Typescript 映射

Posted

技术标签:

【中文标题】优化 JSON 到 Typescript 映射【英文标题】:Optimise JSON to Typescript Map 【发布时间】:2020-07-20 12:01:00 【问题描述】:

我将一个对象从 Java 传递到具有“地图”属性的 Typescript。我的打字稿模型是由接口而不是类来描述的:

export interface Foo 
    bar: Map<string, Bar>;
    mapOfBar: Map<string, Map<string, Bar>>;


export interface Bar 
    someField: string;

我编写了查询 JSON 对象并将其转换为 Foo 的代码:

import HttpClient from '@angular/common/http';
...
export class FooService 
    constructor(private http: HttpClient) 
    

    getFoo(): Observable<Foo> 
        return this.http.get<Foo>('/some/route/to/FooEndpoint');
        

这将返回对象,但 barmapOfBar 是对象类型,而不是 Map。所以在网上搜索了很多之后,我想出了这个代码:

    getFoo(): Observable<Foo> 
        return this.http
            .get<Foo>('/some/route/to/FooEndpoint')
            .pipe(
                map((res: Foo) => 
                    res.bar = new Map(Object.entries(res.bar));

                    let actualMapOfBar = new Map(Object.entries(res.mapOfBar));
                    res.mapOfBar = new Map();
                    for (let [key, bar] of actualMapOfBar) 
                        res.mapOfBar.set(key, new Map(Object.entries(bar)));
                    

                    return res;
                )
            );
    

这会将barmapOfBar 作为地图返回给我,这很棒。

称我为完美主义者,但不知何故这让人觉得笨拙和错误:

map((res: Foo) =&gt; 行表明res 已经实现了Foo 接口,但它没有——它只包含属性,但不包含正确的类型(还)。 这需要设置相当多的转换逻辑,尤其是在处理深度嵌套的对象树时。

有没有更简单的方法来实现这一点,或者可以优化吗?理想情况下,是否有一些“自动”的方式使用 Typescript 接口来转换对象?

(Angular 9.1,Typescript 3.8,rxjs 6.5)

编辑: 这是端点返回的示例响应:

"bar":"key1":"someField":"A","key2":"someField":"B","key3":"someField":"C","mapOfBar":"A":"key1":"someField":"A","key2":"someField":"B","key3":"someField":"C","B":"key1":"someField":"A","key2":"someField":"B","key3":"someField":"C","C":"key1":"someField":"A","key2":"someField":"B","key3":"someField":"C"

【问题讨论】:

【参考方案1】:

不,没有。 map((res: Foo) 行是-你-告诉编译器:“嘿,这是'Foo'类型”,编译器接受这个,因为他信任你。但是,您对编译器撒了谎,因为 API 的响应只是一个普通的 JSON 对象。这不能包括Map 或任何其他类。

就像我之前说的,没有自动的方法来进行转换。你必须自己做这件事。这意味着,为了使其类型安全,您应该有两个接口。一种是FooResponse,另一种是您希望将此Foo 转换为的一种。这不存在的原因是因为TypeScript 类型仅在编译时存在。在运行时,因此当您消化 API 时,类型会丢失,您的转换信息也会丢失。这就是为什么您必须手动执行此操作的原因。

一个小问题。为什么要它们成为地图?除了一些相对方便的 API,你也可以保持原样,将你的类型改为:

export interface Foo 
    bar: Record<string, Bar>;
    mapOfBar: Record<string, Record<string, Bar>>;


export interface Bar 
    someField: string;

【讨论】:

感谢您的详细回答,这真的很有帮助。你是对的,我使用 Map 是因为有用的辅助方法。但是,正如您所说,在这种情况下使用 Record 更有意义,因为我不需要进行手动转换。【参考方案2】:

有没有更简单的方法来实现这一点,或者可以优化吗?理想情况下,是否有一些“自动”的方式使用 Typescript 接口来转换对象?

是的,是的!它被称为 tapi.js - 它是一个轻量级的自动映射器来/从 JSON 数据。

npm i -D tapi.js

然后您可以通过多种方式映射您的对象,即:

let typedObject = new YourClass().fromJSON(jsonData);

或者,更简单地说,您可以像这样映射承诺的内容

http.YOUR_REQUEST_CODE_HERE
    .as(YourClass)
    .[then/pipe/whatever](typedObject =>  ... )

您可以阅读the docs 了解更多信息。

【讨论】:

以上是关于优化 JSON 到 Typescript 映射的主要内容,如果未能解决你的问题,请参考以下文章

将 JSON 对象映射到 TypeScript 对象

如何在 TypeScript Angular 2 中将 Json 对象数组映射到另一个普通 json 对象

json 2 typescript 映射给出类型错误

如何在 Angular 2 的 TypeScript 中映射嵌套的 Json

更改(映射)JSON列表形状 - 在TypeScript中向对象添加标记

在 Typescript 中解析 JSON 对象