关键字:使用TypeScript模块中的类型而不导入,仅发布具有类型的包,告诉TypeScript在NPM模块中查找类型。
我想发布一个NPM模块,其中包含可全局访问的类型,就像lib.d.ts
。
该模块应具有什么结构,以及如何将其包含在另一个项目中?
如果要使这些类型在全局范围内可见实在太难了,则只需使用<reference/>
就可以了,但这在我尝试时不起作用。
在我要使用类型的项目中,我有一个src
文件夹,其中包含所有源代码,还有一个bin
文件夹,其中包含{ {1}}。
包含类型的模块实际上可以具有任何结构,只要它可以工作,我就不在乎。
到目前为止,我已经尝试了很多组合,包括tsc
设置类型,export
设置类型,declare
设置类型,将其放入export declare
或到.ts
文件中,将它们在.d.ts
内的软件包文件夹中移动,node_modules
对其进行打包,import
对其进行打包,然后将其放入<reference/>
……但是没有任何效果。而且缺少好的文档也无济于事。
答案 0 :(得分:2)
我必须为我的日志记录库winston-jsonl-logger
解决此问题。它使用称为logger
的全局变量来扩展全局范围。我同意这是TypeScript中最难解决的问题(如果不是最难解决的话),尤其是因为缺少足够的文档。在此示例中,我创建了一个使用全局可见(“脚本”)和模块可见(“模块”)类型的库。为了澄清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
,它将 still 键入为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
;否则就这些了。</ p>
tsconfig.json
没什么特别的。只是您的标准tsc --init
默认值。
只需确保您添加的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 –可能仍然是一个问题)。 / p>
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!
希望这会有所帮助!
答案 1 :(得分:1)
花几天时间弄清楚。我发现了两种方法可以做到这一点:
作品就像一个饰物,这里不再详述。
对于解决方案B:
my-module/<DIST>/index.d.ts
my-module/globalTypes/index.d.ts
中编写另一个声明文件,它将公开全局名称空间:// access from window.MyModule
interface Window {
MyModule: import('my-module/DIST_FOLDER').MyModule
}
// or directly MyModule
declare const MyModule: import('my-module/DIST_FOLDER').MyModule
也许您发现了它。但是,是的,您必须将全局声明文件放置在模块的子文件夹中。
为什么呢因为typesRoot
指令对您指向的文件夹的子代进行爬网。
这意味着,在您的主项目中,当您设置时:
{
"typeRoots": ["./node_modules/@types", "./node_modules/my-module"]
}
TSC将找到./node_modules/my-module/globalType/index.d.ts
,但找不到./node_modules/my-module/index.d.ts
实际上,这是逻辑,但是您可以(太)在doc中轻易地错过它。
默认情况下,TSC使用值:"typeRoots": ["./node_modules/@types"]
。并且@types文件夹中没有声明。
因此,它对您指定的所有路径都起作用。
答案 2 :(得分:1)
与这里的其他响应者类似,我也花了相当多的时间试图做到这一点?。我的用例略有不同,我根据我看到的其他库所做的事情找到了另一种方法。
发布以防这对其他人有用。
我的目标与 OP 的目标略有不同:我想发布全局接口类型,并且让我的库的下游用户在编写类型时随时可用,但我不想增加 window
或global
,只需像 React 一样发布全局环境类型(例如,您在编写类型时不必 import React
使用 React.ComponentType
)。
我是这样做的:
ambient.d.ts
并将其放在我的项目文件夹的根目录中。此文件将与 dist
文件夹一起发布。/index.d.ts
),重新导出您编译的入口点 (./dist/index.d.ts
) 并在其中包含一个三斜杠引用 (/// <reference types="./ambient" />
)您的环境类型文件。还要确保您的 tsconfig.json
的 include
选项具有 ambient.d.ts
。.d.ts
文件,其中包含对您的 lib 的三斜杠引用。许多 create-*-app
初学者已经创建了这个文件。例如,create-next-app
创建一个 next-env.d.ts
文件,该文件已经包含三斜杠引用。您可以告诉您的用户加强这一点。.
├── README.md
├── package-lock.json
├── package.json
├── src
│ ├── index.ts
│ └── initLoggers.ts
├── dist
│ ├── index.d.ts <-- your compiled index.d.ts file
│ ├── index.js
│ ├── index.js.map
│ ├── foo.d.ts
│ ├── foo.js
├── ambient.d.ts <-- write global types here
├── index.d.ts <-- new types entry point
├── tsconfig.json
您将发布 package.json
、ambient.d.ts
、index.d.ts
以及 dist
中的所有内容。
/package.json
:{
// ...
"types": "./index.d.ts", // specify the new entry types entry-point
// ...
}
/index.d.ts
和 tsconfig.json
(第 2 步):// this is the new entry-point for your types
// use the triple-slash reference to bring in your ambient types
/// <reference types="./ambient" />
// re-export your compiled types
export * from './dist';
{
"compilerOptions": { /* ... */ },
"include": ["./src", "./ambient.d.ts"]
}
告诉他们创建一个 blah.d.ts
文件并向您的库添加三斜杠引用。如上所述,next.js 已经有了这个文件,它叫做 next-env.d.ts
。您可以告诉您的用户对其进行扩充或创建一个新的 *.d.ts
文件。
/// <reference types="next" />
/// <reference types="next/types/global" />
// ???
/// <reference types="your-published-lib-name" />
// ???
<块引用>
? 或者,正如其他答案所建议的那样,您可以告诉您的用户将您的库添加到 typeRoots
中的 tsconfig.json
编译器选项,但我更喜欢三斜杠引用,因为它不会改变默认编译器选项,这是我在 next.js 等其他库中看到的。