Оптимизируйте JSON для карты Typescript - PullRequest
1 голос
/ 08 апреля 2020

Я передаю объект из 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');
    }    
}

Это возвращает мне объект, но bar и mapOfBar имеют тип объекта, а не Map. Так что после недолгих поисков в inte rnet я получил следующий код:

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

Это возвращает мне bar и mapOfBar в виде Карт, что здорово.

Назовите меня перфекционистом, но почему-то это кажется неуклюжим и неправильным:

  • Строка map((res: Foo) => { указывает, что res уже реализует интерфейс Foo, но это не так - это только содержит свойства, но не правильные типы (пока).
  • Это довольно много логик преобразования c для настройки, особенно при работе с глубоко вложенными деревьями объектов.

Нет ли более простого способа реализовать это или его можно оптимизировать? Есть ли в идеале какой-нибудь "автоматический" способ использования интерфейса Typescript для преобразования объекта?

(Angular 9.1, Typescript 3.8, rx js 6.5)

EDIT: Это пример ответа, возвращаемого конечной точкой:

{"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 Ответ

3 голосов
/ 08 апреля 2020

нет там нет. Строка map((res: Foo) - это вы говорите компилятору: «эй, это типа« Foo »», и компилятор принимает это, потому что он доверяет вам. Тем не менее, вы солгали компилятору, потому что ответ API - это просто простой JSON объект. Это не может включать Map или любой другой класс.

Как я уже говорил ранее, нет автоматического c способа сделать преобразование. Вам придется сделать это самостоятельно. Это означает, что для обеспечения безопасности при вводе текста у вас должно быть два интерфейса. Один из них - FooResponse, а другой - в который вы хотите преобразовать Foo. Причина этого не в том, что TypeScript типы существуют только во время компиляции. Поэтому во время выполнения, когда вы перевариваете API, типы теряются, как и ваша информация для преобразования. Вот почему вы должны сделать это вручную.

Небольшой вопрос. Почему вы хотите, чтобы они были картами? Помимо некоторого относительно удобного API, вы также можете просто сохранить его в том виде, в котором он есть, и изменить тип ввода на:

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

export interface Bar {
    someField: string;
}
...