Javascript / Typescript: преобразование одного класса Array в другой в Angular - PullRequest
0 голосов
/ 20 февраля 2020

Как я могу сопоставить список массивов от источника до места назначения с двумя классами ниже? Нужен DTO для сопоставления с массивом поиска. Была проверка функции карты, до сих пор не работает. Кроме того, должны ли мы сделать метод mapFromSourceAddressDto отдельной функцией Export или внутри самого класса Lookup, будет ли это проще?

Возможно, что модели DTO API бэкенда могут измениться, поэтому попытайтесь создать безопасный для типов способ сопоставления столбцов подмножества и получите уведомление об изменении имени столбца DTO.

export class SourceOfAddressDto {
    sourceOfAddressId: number | undefined;
    sourceOfAddressCode: string | undefined;
    sourceOfAddressDescription: string | undefined;
    sourceOfAddressLabel: string | undefined;
    createDate: date;
    userId: string;
}

export class SourceOfAddressLookup {
    sourceOfAddressId: number | undefined;
    sourceOfAddressCode: string | undefined;
    sourceOfAddressDescription: string | undefined;
    sourceOfAddressLabel: string | undefined;       
}

export function mapFromSourceOfAddressDto(sourceOfAddressDto: SourceOfAddressDto){
    const sourceOfAddressLookup = new SourceOfAddressLookup();
    sourceOfAddressLookup .sourceOfAddressId = sourceOfAddressDto.sourceOfAddressId
    sourceOfAddressLookup .sourceOfAddressCode = sourceOfAddressDto.sourceOfAddressCode;
    sourceOfAddressLookup .sourceOfAddressDescription = sourceOfAddressDto.sourceOfAddressDescription
    sourceOfAddressLookup .sourceOfAddressLabel = sourceOfAddressDto.sourceOfAddressLabel;

    return sourceOfAddressLookup ;
}

Цель: Take an Array<SourceofAddressDto> ---> Array<SourceOfAddressLookup>

Попытка решения:

в поисках более чистого пути,

public addressSourceList: Array<SourceOfAddressLookup>; 

if (addressSourceListDto.length > 0){
    for (let i = 0; i < addressSourceListDto.length; ++i ) {
      this.addressSourceList[i] = mapFromSourceOfAddressDto(addressSourceListDto[i])
    }
  }

Ответы [ 2 ]

2 голосов
/ 20 февраля 2020

Я думаю, что вы, возможно, делаете вещи более сложными, чем нужно.

Более эффективным способом обеспечения безопасности типов в этом сценарии было бы сначала определить два интерфейса машинописи.

Один для вашей структуры данных DTO (который я предполагаю, приходит из запроса API). Другой для вашей структуры объекта 'destination'.

interface SourceOfAddressDto {
  sourceOfAddressId?: number;
  sourceOfAddressCode?: string;
  sourceOfAddressDescription?: string;
  sourceOfAddressLabel?: string;
  createDate: string;
  userId: string;
}

interface SourceOfAddress {
  sourceOfAddressId?: number;
  sourceOfAddressCode?: string;
  sourceOfAddressDescription?: string;
  sourceOfAddressLabel?: string;
}

Вы можете определить свою функцию карты отдельно с указанным типом возврата

const mapItems = (item:SourceOfAddressDto):SourceOfAddress[] => {
  return {
    sourceOfAddressId: item.sourceOfAddressId;
    sourceOfAddressCode: item.sourceOfAddressCode;
    sourceOfAddressDescription: item.sourceOfAddressDescription;
    sourceOfAddressLabel: item.sourceOfAddressLabel;
  }
};

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

const data = await fetch("http://api") as SourceOfAddressDto[];
const mappedData = data.map(mapItems);
1 голос
/ 21 февраля 2020

НОВЫЙ ОТВЕТ:

Хорошо, я переключаю это: ваш DTO - это интерфейс, соответствующий обычным-старым - javascript -объектам, которые вы получаете из своего API:

interface SourceOfAddressDto {
    sourceOfAddressId?: number | undefined;
    sourceOfAddressCode?: string | undefined;
    sourceOfAddressDescription?: string | undefined;
    sourceOfAddressLabel?: string | undefined;
    createDate: Date;
    userId: string;
}

И ваш Lookup - это настоящий класс с методами, которые вам нужно использовать, например, shout(), который я приведу в качестве примера:

class SourceOfAddressLookup {
    sourceOfAddressId?: number | undefined;
    sourceOfAddressCode?: string | undefined;
    sourceOfAddressDescription?: string | undefined;
    sourceOfAddressLabel?: string | undefined;
    shout() {
        console.log("HELLO MY ID IS " + this.sourceOfAddressId +
            " AND MY CODE IS \"" + this.sourceOfAddressCode + "\"" +
            " AND MY DESCRIPTION IS \"" + this.sourceOfAddressDescription + "\"" +
            " AND MY LABEL IS \"" + this.sourceOfAddressLabel + "\"!");
    }
}

Вместо pluck(), как прежде Я определю assignProps(), который принимает целевой объект, исходный объект и список ключей свойств для копирования из источника в место назначения. Это generic c, поэтому компилятор должен кричать на вас, если по какой-то причине свойства источника не соответствуют типу назначения:

function assignProps<T, K extends keyof T>(
    destination: T, 
    source: Pick<T, K>, 
    ...keys: K[]
): T {
    keys.forEach(k => destination[k] = source[k]);
    return destination;
}

Итак, mapFromSourceOfAddressDto принимает SourceOfAddressDto и вызывает assignProps() на только что созданном экземпляре SourceOfAddressLookup:

const mapFromSourceOfAddressDto = (dto: SourceOfAddressDto) => assignProps(
    new SourceOfAddressLookup(),
    dto,
    "sourceOfAddressId",
    "sourceOfAddressCode",
    "sourceOfAddressDescription",
    "sourceOfAddressLabel"
)

Компилируется без ошибок, поэтому типы должны работать. Затем вы можете довольно просто выполнить сопоставление массива, например:

class Foo {
    public addressSourceList: Array<SourceOfAddressLookup>;
    constructor(addressSourceListDto: Array<SourceOfAddressDto>) {
        this.addressSourceList = addressSourceListDto.map(mapFromSourceOfAddressDto);
    }
}

И давайте проверим это, построив что-то с массивом объектов SourceOfAddressDto:

const foo = new Foo([{
    createDate: new Date(),
    userId: "abc",
    sourceOfAddressId: 0,
    sourceOfAddressDescription: "d",
    sourceOfAddressCode: "c",
    sourceOfAddressLabel: "l"
}, {
    createDate: new Date(),
    userId: "def",
    sourceOfAddressId: 1,
    sourceOfAddressDescription: "D",
    sourceOfAddressCode: "C",
    sourceOfAddressLabel: "L"
}]);

Это должно быть отображено с помощью Конструктор Foo для массива SourceOfAddressLookup экземпляров, поэтому давайте проверим его, вызвав метод shout() для каждого элемента:

foo.addressSourceList.forEach(x => x.shout())
// HELLO MY ID IS 0 AND MY CODE IS "c" AND MY DESCRIPTION IS "d" AND MY LABEL IS "l"! 
// HELLO MY ID IS 1 AND MY CODE IS "C" AND MY DESCRIPTION IS "D" AND MY LABEL IS "L"!

Хорошо, выглядит хорошо. Удачи снова!

Ссылка на игровую площадку с кодом


СТАРЫЙ ОТВЕТ:

Я предполагаю, что ваш DTO является полноценным классом с методы

class SourceOfAddressDto {
    sourceOfAddressId: number | undefined;
    sourceOfAddressCode: string | undefined;
    sourceOfAddressDescription: string | undefined;
    sourceOfAddressLabel: string | undefined;
    createDate: Date;
    userId: string;
    constructor(createDate: Date, userId: string) {
        this.createDate = createDate; this.userId = userId;
    }
}

, но тип поиска может быть интерфейсом, который просто имеет некоторые из тех же свойств. Если вы хотите, чтобы у этого типа было имя, вы можете сделать это следующим образом:

// if you must name this type, you can do this:
interface SourceOfAddressLookup extends Pick<SourceOfAddressDto,
    "sourceOfAddressCode" |
    "sourceOfAddressDescription" |
    "sourceOfAddressId" |
    "sourceOfAddressLabel"
    > { }

В общем, я бы использовал функцию типа pluck(), чтобы взять существующий объект и создать новый, скопировав список свойств:

function pluck<T, K extends keyof T>(t: T, ...k: K[]) {
    return k.reduce((a, k) => (a[k] = t[k], a), {} as Pick<T, K>)
}

И тогда ваша функция mapFromSourceOfAddressDto может использовать ее следующим образом:

function mapFromSourceOfAddressDto(obj: SourceOfAddressDto): SourceOfAddressLookup {
    return pluck(
        obj,
        "sourceOfAddressId",
        "sourceOfAddressCode",
        "sourceOfAddressDescription",
        "sourceOfAddressLabel"
    );
}

И мы можем убедиться, что она работает:

const dto = new SourceOfAddressDto(new Date(), "abc");
dto.sourceOfAddressCode = "cod";
dto.sourceOfAddressDescription = "descrip";
dto.sourceOfAddressLabel = "lab";
dto.sourceOfAddressId = 1;

const lookup = mapFromSourceOfAddressDto(dto);

console.log(JSON.stringify(lookup));

/* {
"sourceOfAddressId":1,
"sourceOfAddressCode":"cod",
"sourceOfAddressDescription":"descrip",
"sourceOfAddressLabel":"lab"
}
*/

Хорошо выглядит для меня. Надеюсь, что это поможет дать вам некоторое направление; удачи!

Детская площадка ссылка на код

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...