Как создать функцию «декоратор» в TypeScript, которая может обрабатывать несколько типов

#typescript

Вопрос:

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

У меня есть очень повторяющийся код, который выглядит примерно так:

 import {EventA, EventB, .... EventG} from './events'
import {EntityA, EntityB, ....EntityG} from './entities'

//all the event objects are defined: EventA extends Event
//all the entity objects are defined: EntityA extends Entity

export function handleEventA(event: eventA): void {
  let entity = new EntityA(event.id)
  entity.date = event.date
  entity.address = event.address
  ...
  entity.SomethingUniqueToEntityA = event.SomethingUniqueToEntityA
  entity.save()
}

...

export function handleEventG(event: eventG): void {
  let entity = new EntityG(event.id)
  entity.date = event.date
  entity.address = event.address
  ...
  entity.SomethingUniqueToEntityG = event.SomethingUniqueToEntityG
  entity.save()
}
 

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

Я думал, что у меня будет какой-нибудь декоратор (или, может быть, фабрика??) функция, которая позволит мне сделать что-то вроде:

 function decorate(entity: Entity, event: Event): Entity {
  entity.date = event.date
  entity.address = event.address
  return entity
}

export function handleEventA(event: eventA): void {
  let entity = new EntityA(event.id)
  entity = decorate(entity, event)
  ...
  entity.SomethingUniqueToEntityA = event.SomethingUniqueToEntityA
  entity.save()
}
 

Или, может быть, даже какая-то заводская функция, которая создаст экземпляры сущностей.

Я попытался определить что-то вроде decorate(entity: EntityA | EntityB |... EntityG, event: EventA | EventB |... EventG) , но это также становится громоздким и повторяющимся.

Моя проблема в следующем: как мне определить decorate функцию, позволяющую использовать все сущности и события в качестве параметров? Нужно ли мне вообще возвращаться entity или это вызывается по ссылке?

Ответ №1:

Самым простым способом должно быть определение только подписи, необходимой для вашей decorate функции. Это возможно благодаря структурной типизации TypeScript. Достаточно, чтобы «форма» типа совпадала — поэтому любое Event ( EventA , EventB ) или Entity имеющее эти 2 свойства может быть передано в качестве входного параметра.

 interface IDateAndAddress {
  date: Date,
  address: string
}

function decorate(entity: IDateAndAddress, event: IDateAndAddress): void {
  entity.date = event.date;
  entity.address = event.address;
}
 

Вы можете быть немного более конкретным, чтобы убедиться, что вы не можете поменять entity местами и event по ошибке, при вызове decorate функции, но это суть решения.

Комментарии:

1. Я не контролирую ни коды сущностей, ни коды событий — они генерируются. Кроме того, свойства не являются свойствами верхнего уровня, например, чтобы получить адрес, мне нужно сделать что-то вроде entity.address = event.block.address .

2. Затем просто сформируйте IDataAndAdress так, как вам нужно, включая вложенные объекты. Форма должна соответствовать свойствам, которыми вы хотите управлять в функции, не более того. Это сработает.

3. Я попробовал ваше решение: определил интерфейс и добавил export function decorate(a: IEntityFields, b: Event) : void {} , что я получаю TS2304: Cannot find name 'IEntityFields' , со строкой, выделяющей IEntityFields — даже если она определена прямо над функцией. Является ли это результатом export ?

4. Это не должно быть результатом экспорта. Трудно сказать, что ты делаешь не так. Может быть, опечатка? Здесь есть рабочий образец, если это поможет.

5. Я ценю демонстрационный код. Я не контролирую компилятор TS здесь (код генерируется библиотекой), но если я использую запятые interface вместо точки с запятой или оставляю его пустым, он жалуется на типы (TS003). Значит, что-то не так.