#node.js #typescript #inheritance #callback
Вопрос:
Я довольно новичок в машинописи и node.js но я хотел чему-то научиться и попробовать что-то новое с angular, node и express. Я все еще пытаюсь найти несколько хороших практик, чтобы разделить проект express на пару более мелких частей, и каким-то образом я столкнулся с проблемой. Я хотел создать абстрактный класс контроллера и некоторые подклассы, которые были бы типичными контроллерами REST, поддерживаемыми express.Router()
.
Вот базовый класс — контроллер:
import express, {Router} from "express";
export abstract class Controller {
public router = express.Router();
public path: string;
protected constructor(path: string) {
this.path = path;
console.log('Calling initializeRoutes() from superclass');
this.initializeRoutes();
}
abstract initializeRoutes(): void;
}
И подкласс — это всего лишь пример — класс UserController:
import {Controller} from "./Controller";
import express from "express";
export class UserController extends Controller {
private users = [
{name: 'User1', password: 'password1'},
{name: 'User2', password: 'password2'},
{name: 'User3', password: 'password3'}
]
constructor(path: string) {
super(path);
console.log('Calling initializeRoutes() from subclass');
this.initializeRoutes();
}
initializeRoutes(): void {
// console.log(this.router);
console.log(this.getAllUsers);
console.log(this.createAPost);
// this.router.get(this.path, this.getAllUsers);
// this.router.post(this.path, this.createAPost);
}
getAllUsers = (request: express.Request, response: express.Response) => {
response.send(this.users);
}
createAPost = (request: express.Request, response: express.Response) => {
const user = request.body;
this.users.push(user);
response.send(user);
}
Основная проблема в том, что я хотел бы позвонить initializeRoutes()
в базовый класс, но когда я это делаю, экспресс выдает ошибку, из-за которой я не могу использовать неопределенные обратные вызовы в маршрутизаторе. Я провел некоторую отладку и обнаружил, что при использовании initializeRoutes()
в базовом классе обратные вызовы из подклассов не определены, в то время как при запуске из подклассов все в порядке:
[1] [nodemon] starting `ts-node serverserver.ts`
[1] Calling initializeRoutes() from superclass
[1] undefined
[1] undefined
[1] Calling initializeRoutes() from subclass
[1] [Function (anonymous)]
[1] [Function (anonymous)]
Это почему? Я делаю что-то не так или что-то упускаю?
Заранее спасибо
ИЗМЕНИТЬ: Я попробовал предлагаемое решение:
export class UserController extends Controller {
constructor(path: string) {
super(path);
this.getAllUsers = this.getAllUsers.bind(this);
this.createAPost = this.createAPost.bind(this);
console.log('Calling initializeRoutes() from subclass');
// this.initializeRoutes();
}
initializeRoutes(): void {
// console.log(this.router);
this.getAllUsers = this.getAllUsers.bind(this);
this.createAPost = this.createAPost.bind(this);
console.log(this.getAllUsers);
console.log(this.createAPost);
this.router.get(this.path, this.getAllUsers);
this.router.post(this.path, this.createAPost);
}
}
Когда я вставляю его в initializeRoutes()
, я получаю:
[1] TypeError: Cannot read property 'bind' of undefined [1] at UserController.initializeRoutes (C:BlazejProjectspasteurservercontrollerUserController.ts:19:41)
Когда я помещаю его в конструктор, я получаю: [1] Error: Route.get() requires a callback function but got a [object Undefined]
Ответ №1:
getAllUsers = (request: express.Request, response: express.Response) => {
response.send(this.users);
}
Этот синтаксис представляет собой «поле класса», которое является функцией, которая находится в процессе добавления в javascript/typescript, но еще не совсем там. Вы можете ознакомиться с предложением здесь
Но даже несмотря на то, что он все еще проходит через процесс предложения, вы можете использовать (и используете) его сегодня, благодаря транспиляторам. Typescript возьмет написанный вами код и превратит его в ближайший эквивалентный код. Например, это:
class Example {
constructor() {
super();
}
someFunction = () => {};
}
Превращается в основном в это:
class Example {
constructor() {
super();
this.someFunction = () => {};
}
}
Но обратите внимание, что транспилированный код помещает создание функции после вызова super(). Так и должно быть, но это означает, что, пока super работает, функция еще не существует.
Поэтому, чтобы функции существовали раньше времени, вам нужно будет использовать обычные методы класса вместо полей класса, как в:
getAllUsers(request: express.Request, response: express.Response) {
response.send(this.users);
}
createAPost(request: express.Request, response: express.Response) {
const user = request.body;
this.users.push(user);
response.send(user);
}
Но, конечно, вы, вероятно, сделали их как функции со стрелками специально, потому что вам нужно значение this
для правильной работы. Поэтому для этого я бы рекомендовал привязать функции. Если вам не нужны связанные функции во время инициализации, вы можете сделать это в конструкторе вашего дочернего класса:
constructor(path: string) {
super(path);
this.getAllUsers = this.getAllUsers.bind(this);
this.createAPost = this.createAPost.bind(this);
}
Или, если они вам понадобятся во время строительства, возможно, сделайте это в initializeRoutes
initializeRoutes(): void {
this.getAllUsers = this.getAllUsers.bind(this);
this.createAPost = this.createAPost.bind(this);
this.router.get(this.path, this.getAllUsers);
this.router.post(this.path, this.createAPost);
}
Комментарии:
1. Спасибо за очень быстрый ответ. Я понятия не имел, что это так работает. Во всяком случае, все еще не работает. Я попробовал оба решения — привязку обратных вызовов в конструкторе и инициализацию маршрутов (), и все равно получил: ` Ошибка: Route.get() требует функции обратного вызова, но получил [объект не определен] «