Как сделать модуль NPM с глобально доступными типами - PullRequest
1 голос
/ 13 апреля 2019

Ключевые слова: использовать типы из модуля TypeScript без импорта, публиковать пакеты только с типами, указывать TypeScript искать типы в модуле NPM.


Я хочу опубликоватьМодуль NPM, который содержит глобально доступные типы, очень похожие на lib.d.ts.

Какую структуру должен иметь модуль и как включить его в другой проект?

Если сделать типы глобально видимыми простослишком сложно, потребовав этого с <reference/> было бы достаточно, но это не сработало, когда я попытался.


В проекте, где я хочу использовать типы, яу вас есть папка src, содержащая весь исходный код, и папка bin, которая содержит выходные данные tsc.

Модуль, содержащий типы, может иметь практически любую структуру, мне на самом деле все равноПока это работает.


До сих пор я пробовал много, много комбинаций, включая export типы, declare типы, export declare типы, их .ts или .d.ts файл, перемещениеони находятся в папке пакета внутри node_modules, import их, <reference/> их, помещая их в rootDirs ... Но ничего не работает.И отсутствие хорошей документации по этому вопросу также не помогло.

1 Ответ

2 голосов
/ 15 апреля 2019

Мне пришлось решить эту проблему для моей библиотеки журналов, winston-jsonl-logger.Он дополняет глобальную область видимости глобальной переменной с именем logger.Я согласен, что это одна из самых сложных (если не самая трудная) проблема в TypeScript, не в последнюю очередь из-за отсутствия достаточного количества документации.В этом примере я создаю библиотеку, которая использует как глобально видимые («скрипт»), так и модульно-видимые («модуль») типы.Чтобы уточнить, что официальная терминология :

В TypeScript, как и в ECMAScript 2015, любой файл, содержащий import или export верхнего уровня, считается модулем.И наоборот, файл без каких-либо объявлений import или export верхнего уровня обрабатывается как сценарий, содержимое которого доступно в глобальной области (и, следовательно, также для модулей).

Структура каталогов

Моя папка src переносится в dist.test игнорируется при трансплантации.

Крайне важно, чтобы ваши наборы имели имя index.d.ts и были вложены в папку, имя которой совпадает с именем вашего проекта (что, вероятно, строго соответствует имени, указанному в * 1023).*).Это то, что структура будет искать typeRoots.

.
├── README.md
├── dist
│   ├── Logger.d.ts
│   ├── Logger.js
│   ├── Logger.js.map
│   ├── initLoggers.d.ts
│   ├── initLoggers.js
│   └── initLoggers.js.map
├── package-lock.json
├── package.json
├── src
│   ├── Logger.ts
│   └── initLoggers.ts
├── test
│   └── index.ts
├── tsconfig.json
└── typings
    └── winston-jsonl-logger
        └── index.d.ts

«скриптовые» наборы

Типы скриптов - это те, в которых отсутствует верхний уровень import или export.Они будут видны в глобальном масштабе в проектах, которые их потребляют.

Конечно, поскольку они не могут использовать объявления верхнего уровня import, они ограничены в своей описательности;Вы можете часто видеть много any используемых здесь.Это проблема, которую я пытаюсь решить в своем собственном вопросе .

// typings/index.d.ts
declare namespace NodeJS {
    export interface Global {
        logger?: any;
        log?: any;
        logInfo?: any;
    }
}

Если вы используете logger в глобальной области видимости, она будет напечатана как anynow.

печатания 'module'

При печатании модуля можно использовать import или export верхнего уровня, но они будут видны только в том случае, если модуль будет импортирован в проект.то есть они не видны глобально по всему проекту.

// initLoggers.ts
import {Logger} from "./Logger";
import {LogEntry, Logger as WinstonLogger} from "winston";

// Now we can be more descriptive about the global typings
declare global {
    const logger: Logger;
    // LogEntry's interface: { level: string, message: string, data?: any }
    function log(entry: LogEntry): WinstonLogger;
    function logInfo(message: string, data?: any): WinstonLogger;
}

export function initLoggers(){
    global.logger = new Logger();
    global.log = logger.log.bind(logger);
    global.logInfo = (message: string, data?: any) => {
        return logger.log({ level: "info", message, data });
    }
}

Если вы используете logger в глобальной области видимости, все равно будет напечатано как any, но по крайней мере global.logger будет иметь правильные наборы.

Чтобы гарантировать, что эти типы будут видны в вашем проекте my-project, убедитесь, что my-project импортирует этот файл из winston-jsonl-logger;Я делаю это в точке входа моего приложения.

package.json

Я не использовал поле typings или types (возможно, указание "typings": "typings/winston-jsonl-logger/index.d.ts" означало бы, что пакеты неЯ не могу явно объявить путь к моим наборам; я не знаю), но я сделал , не забудьте распространить свою папку с наборами.

{
  "name": "winston-jsonl-logger",
  "version": "0.5.3",
  "description": "TypeScript JSONL logger.",
  "main": "dist/Logger.js",
  "files": [
    "dist",
    "typings"
  ],
  "devDependencies": {
    "@types/logform": "1.2.0",
    "@types/node": ">=9.6.21",
    "ts-node": "7.0.1",
    "typescript": "3.1.1"
  },
  "dependencies": {
    "winston": "3.2.0",
    "winston-daily-rotate-file": "3.6.0",
    "winston-elasticsearch": "0.7.4"
  }
}

Пропущенные поля: repository, keywords, author, license, homepage, publishConfig и scripts;в противном случае это все.

tsconfig.json

Для самой библиотеки

Ничего особенного.Просто ваши стандартные tsc --init значения по умолчанию.

Для проектов, использующих lib

Просто убедитесь, что вы добавили typeRoots выглядит следующим образом:

{
  "compilerOptions": {
    // ...All your current fields, but also:
    "typeRoots": [
      "node_modules/@types",
      "node_modules/winston-jsonl-logger/typings/winston-jsonl-logger"
    ]
  }
}

Если выВы используете ts-node

Здесь есть другие ошибки.По умолчанию ts-node игнорирует типизацию сценариев и импортирует только потомки импорта начального уровня (причина этого в скорости / эффективности).Вы можете принудительно разрешить импорт так же, как tsc, установив переменную окружения: TS_NODE_FILES=true.Да, тесты будут выполняться медленнее, но, с другой стороны, это будет работать вообще.

Если вы используете ts-node через командную строку, объявите переменную окружения TS_NODE_FILES равной true.Мне также пришлось объявить TS_NODE_CACHE как false из-за необъяснимой ошибки кэша в ts-node (версия 7.0.1 - все еще может быть проблемой) при разрешении импорта / зависимостей.

TS_NODE_FILES="true" TS_NODE_CACHE="false" TS_NODE_PROJECT="./tsconfigs/base.json" /usr/bin/nodejs --require ts-node/register --inspect=127.0.0.1:9231 src/index.ts --myCustomArg="hello"

Я обычно использую ts-node, потому что я тестирую с Мокко.Вот как я передаю переменные окружения в ts-node от Mocha:

// mocha.env.js

/* From: https://github.com/mochajs/mocha/issues/185#issuecomment-321566188
 * Via mocha.opts, add `--require mocha.env` in order to easily set up environment variables for tests.
 *
 * This can theoretically be made into a TypeScript file instead, but it seemed to not set the env variable when I tried;
 * perhaps it failed to respect the order of the --require declarations. */
process.env.TS_NODE_FILES = "true"; // Force ts-node to use TypeScript module resolution in order to implictly crawl ambient d.ts files
process.env.TS_NODE_CACHE = "false"; // If anything ever goes wrong with module resolution, it's usually the cache; set to false for production, or upon any errors!

Надеюсь, это поможет!

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