Как создать экспресс-приложение с импортом вместо require

#typescript #express

#typescript #экспресс

Вопрос:

Это то, что работает с require . Вместо этого мы хотим, чтобы оно использовало import .

 import { Request, Response, Application } from 'express';

// TODO Figure out how NOT to use require here.
const express = require('express');
var app: Application = express();

app.get('/', function (req: Request, res: Response) {
  res.send('Hello World')
});

app.listen(3000);
  

Что мы пробовали

В нашем tsconfig.json есть "esModuleInterop": true .

Попытка # 1

 import express from 'express';
  

Это выдает эту ошибку:

«node_modules/@types/express /index»‘ не имеет файла export.ts по умолчанию

Попытка # 2

 import * as express from 'express';
var app = express();
  

Это выдает другую ошибку:

Не удается вызвать выражение, у типа которого отсутствует сигнатура вызова. Тип ‘typeof e’ не имеет совместимых подписей вызовов.ts(2349) index.ts(1, 1): Тип создается при этом импорте. Импорт в стиле пространства имен не может быть вызван или сконструирован и вызовет сбой во время выполнения. Рассмотрите возможность использования импорта по умолчанию или импорта require здесь вместо этого.

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

1. Я могу рассказать вам, почему попытка # 2 не сработала: вы запросили объект пространства имен модуля, затем попытались запустить его как функцию.

2. Похоже, @types/express отсутствует тот факт, что Express предоставляет экспорт по умолчанию, который является функцией. (Я только что дважды проверил, что это так, и это import express from "express"; правильно извлекает его в коде JavaScript с --experimental-modules включенным.) Так что определенно дело в типах (как следует из ошибки). Заглядывая в node_modules/@types/express/index.d.ts , я вижу, что он, по крайней мере, пытается определить экспорт для экспресс-функции: «Создает экспресс-приложение. Функция express() — это функция верхнего уровня, экспортируемая модулем express.»

3. @T.J.Crowder Принудительное приведение к функции работает: const app = (express as unknown as Function)(); . Хотя это, безусловно, уродливый взлом.

4. (Говоря о взломах: import * as mno from 'express'; const express = mno.default as unknown as Function; ) Но реальное решение (с которым, к сожалению, я не могу помочь) заключается в сортировке типов в @types/express/index.d.ts и / или их использовании TypeScript.

5. Сортировка типов была бы полезным запросом на извлечение. Я могу добавить это к моему списку дел «может быть, когда-нибудь».

Ответ №1:

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

 import { Request, Response, Application } from 'express';
import express = require('express');

var app: Application = express();

app.get('/', function (req: Request, res: Response) {
  res.send('Hello World')
});

app.listen(3000);
  

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

 // tsconfig.json
{
  "compilerOptions": {
    "allowSyntheticDefaultImports": true,     /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
    "esModuleInterop": true,                  /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
  }
}
  

и импортировать, используя этот импорт по умолчанию:

 import express from 'express' 
var app = express();
  

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

1. Это полезно. Однако в нашем случае мы ориентируемся на модули ECMAScript и видим это сообщение об ошибке: «Назначение импорта не может быть использовано при таргетинге на модули ECMAScript …»

2. @ShaunLuttin Боюсь, что использование bare require() — лучший способ для вас. В качестве альтернативы, если вы используете webpack или babel, вы могли бы предварительно обработать модуль express , чтобы «включить» его в экспорт по умолчанию, но это, вероятно, слишком хлопотно и может привести к путанице в типах.

3. @ShaunLuttin Вы уверены, что хотите настроить таргетинг на модули ECMAScript в своем серверном коде? Узел использует commonjs. Вероятно, вы объединяете все модули перед их запуском с node, так что это не имеет значения, но опять же, зачем использовать какой-то определенный тип модулей, отличных от собственных модулей nodejs? Зачем вставлять круглый колышек в квадратное отверстие, если в итоге вы собираетесь залить все это бетоном?

4. Это потрясающий момент в использовании commonjs на стороне сервера. Я пошел дальше и попробовал это. Поскольку мы используем babel/plugin-transform-typescript для переноса вместо использования tsc , возникает новая ошибка, поскольку система сборки babel не позволяет import = работать. Мы могли бы установить --allowSyntheticDefaultImports , но это (раньше) имело некоторые крайние случаи, которых мы хотим избежать.

5. Комбинация использования commonjs и настройки allowSyntheticDefaultImports , и esModuleInterop того и true другого, похоже, работает.

Ответ №2:

Для того, чтобы иметь возможность импортировать express в обычный js-проект с:

 import express from 'express';
  

Это тоже можно сделать:

  1. Задайте пару ключ-значение:
 "type": "module",
  

в вашем package.json файле

  1. Запустите ваше приложение с помощью команды
 node --experimental-specifier-resolution=node index.js
  

Ответ №3:

Мое выдавало ту же ошибку, возможно, из-за используемой версии, я решил::

 const express = require('express');
const app = express();
const PORT = 3000;
  

e нет package.json: «узел index.js «