Нестатический метод не видит нестатическое поле того же TypeScript-класса, когда вызывается из декоратора

Приведённую ниже функцию планируется использовать как декоратор для методов наследников класса Controller:

function RouteHandler(route: Router.Route): (controller: Controller, methodName: string) => void {

  return (controller: Controller, methodName: string): void => {

    /* В данном случае, "methodName" должен быть у инстанса "controller" покуда `RouteHandler`
       используется по назначению. */
    const handler: Router.RouteHandler =
        (controller as unknown as { [methodName: string]: Router.RouteHandler; })[methodName];

    controller.setRouterAndHandlerPair(route, handler);
  };
}

Эта функция-декоратор должна сохранить объект route и соответствующий ей обработчик маршрута в поле routesAndHandlersMap наследника классаController:

class Controller {

  private readonly routesAndHandlersMap: Map<Router.Route, Router.RouteHandler> = new Map<Router.Route, Router.RouteHandler>();

  public setRouterAndHandlerPair(route: Router.Route, handler: Router.RouteHandler): void {
    this.routesAndHandlersMap.set(route, handler);
  }

  public getRoutesAndHandlers(): Array<[ Router.Route, Router.RouteHandler ]> {
    return Array.from(this.routesAndHandlersMap.entries());
  }
}

Использовать декторатор планируется примерно так:

class ProductsController extends Controller {

  @RouteHandler({
    type: HTTP_Methods.get,
    pathTemplate: "api/products"
  })
  private async retrieveSelection(_request: Request, response: Response): Promise<void> {
    // ...
  }
}

Проблема

Когда я создаю инстанс класса ProductController (унаследованного от Сontroller):

const productController: ProductsController = new ProductsController();

RouteHandler вызывает controller.setRouterAndHandlerPair(route, handler);, а тот в свою очередь вызываетthis.routesAndHandlersMap.set(route, handler);, но this.routesAndHandlersMap по неизвестной мне причине имеет значение undefined. Это порождает ошибку TypeError: Cannot read property 'set' of undefined.

Очень похоже на то, что ссылка не на тот this. Такое обычно происходит, когда вместо вызова функции указывают ссылку на на неё (например this.exampleFuntion1 вместо this.exampleFunction1()), но в данном случае проблема в this стала для меня неожиданной. Согласно выводу в консоль, this имеет значение ProductsController: {} в setRouterAndHandlerPair.

Как будет использован метод getRoutesAndHandlers

Класс Server берёт на себя создание инстансов контроллеров и вызов getRoutesAndHandlers у каждого из них. Всё. что остаётся сделать пользователю класса Server - указать в свойстве routing ссылки на классы контроллеров, которых в реальном приложении несколько:

Server.initializeAndStart({
  host: "127.0.0.1",
  port: 1337,
  routing: [ ProductsController, ProductsCategoriesController ],
  logger: ConsoleApplicationLogger
});

? Полный листинг


Ответы (2 шт):

Автор решения: Александр

У вас routesAndHandlersMap поле объявлено как private, а значит оно доступно только внутри того класса, в котором обьявлено.

Для того, чтобы поле было доступно внутри потомка (т.е в данном случае внутри ProductsController), следует использовать для него модификатор protected

→ Ссылка
Автор решения: Grundy

В данном случае неверное используются параметры декоратора:

Первым параметром передается не создаваемый объект, а прототип этого объекта.

Так как поле routesAndHandlersMap расположено непосредственно в объекте, а не в прототипе и возникает указанная ошибка.

→ Ссылка