我必须为我的日志库 winston-jsonl-logger 解决这个问题。它使用名为logger 的全局变量来扩充全局范围。我同意这是 TypeScript 中最难(如果不是最难)的问题之一,尤其是因为缺乏足够的文档。在这个例子中,我创建了一个同时使用全局可见('script')和模块可见('module')类型的库。澄清official terminology:
在 TypeScript 中,就像在 ECMAScript 2015 中一样,任何包含顶级 import 或 export 的文件都被视为一个模块。相反,没有任何顶级 import 或 export 声明的文件被视为其内容在全局范围内可用的脚本(因此也可用于模块)。
目录结构
我的src 文件夹被转换为dist。 test 从编译中被忽略。
您的输入必须命名为index.d.ts,并嵌套在与您的项目名称相同的文件夹中(严格来说,这可能是package.json 中指定的名称)。这就是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。这是我正在努力解决的问题in my own question。
// typings/index.d.ts
declare namespace NodeJS {
export interface Global {
logger?: any;
log?: any;
logInfo?: any;
}
}
如果您在全局范围内使用logger,现在将键入any。
“模块”类型
模块类型可以使用顶级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 忽略脚本类型,只导入入门级导入的后代(原因是速度/效率)。您可以通过设置环境变量:TS_NODE_FILES=true 来强制它解析导入,就像 tsc 所做的那样。是的,它会更慢地运行测试,但另一方面,它完全可以工作。
如果您通过命令行使用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,因为我正在使用 Mocha 进行测试。以下是我将环境变量从 Mocha 传递给 ts-node 的方法:
// 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!
希望这会有所帮助!