我阅读了TypeScript模块解析的工作原理。
我有以下存储库:@tsstack/di。编译后,目录结构如下:
├── dist
│ ├── annotations.d.ts
│ ├── annotations.js
│ ├── index.d.ts
│ ├── index.js
│ ├── injector.d.ts
│ ├── injector.js
│ ├── profiler.d.ts
│ ├── profiler.js
│ ├── providers.d.ts
│ ├── providers.js
│ ├── util.d.ts
│ └── util.js
├── LICENSE
├── package.json
├── README.md
├── src
│ ├── annotations.ts
│ ├── index.ts
│ ├── injector.ts
│ ├── profiler.ts
│ ├── providers.ts
│ └── util.ts
└── tsconfig.json
在package.json中,我写了“main”:“dist/index.js”。
在Node.js中,一切正常,但TypeScript:
import {Injector} from '@ts-stack/di';
找不到模块“@ts stack/di”的声明文件/path/to/node_modules/@tsstack/di/dist/index.js”隐式具有“any”类型。
然而,如果我按如下方式导入,那么一切都正常:
import {Injector} from '/path/to/node_modules/@ts-stack/di/dist/index.js';
我做错了什么?
在许多项目中,我面临着许多包的相同问题。所以我创建了Declarator,一个自动生成类型声明的npm包。
它基本上通过在后台运行tsc-emitDeclarationOnly来工作。
您可以从npm安装它:
npm install --save-dev declarator
yarn add -D declarator
然后创建一个简单的声明器.json文件:
{
"$schema": "https://raw.githubusercontent.com/ArthurFiorette/declarator/master/schema.json",
"packages": ["package1","package2"]
}
并创建一个脚本来运行它:
使用postinstall脚本将在每次安装包时运行它,这可能很有用
{
"scripts": {
"postinstall": "declarator"
}
}
它不会生成强大的类型,在这一过程中您可能会遇到许多类型,但使用它比不使用它要好得多
阅读更多信息:https://github.com/ArthurFiorette/declarator#readme
如果您正在导入一个第三方模块“foo”,该模块在库本身或@types/foo包(从DefinelyTyped存储库生成)中不提供任何类型,那么可以通过在扩展名为.d.ts的文件中声明该模块来消除此错误。TypeScript在与普通.ts文件相同的位置查找.d.ts文件:如tsconfig.json中的“files”、“include”和“exclude”下所指定。
// foo.d.ts
declare module 'foo';
然后,当您导入foo时,它将被键入为any。
或者,如果你想自己打字员,你也可以这样做:
// foo.d.ts
declare module 'foo' {
export function getRandomNumber(): number
}
然后将正确编译:
import { getRandomNumber } from 'foo';
const x = getRandomNumber(); // x is inferred as number
您不必为模块提供完整的打字员,只需为您实际使用的位提供足够的打字(并且需要正确的打字),因此如果您使用的API量相当少,那么这特别容易。
另一方面,如果您不关心外部库的打字员,并且希望将所有没有打字员的库作为任何库导入,则可以将其添加到扩展名为.d.ts的文件中:
declare module '*';
这样做的好处(也有缺点)是,您可以完全导入任何内容,TS将进行编译。
我在使用节点模块和用typescript编写的react应用程序时遇到了同样的问题。使用npm i-save my module成功安装了模块。它是用javascript编写的,并导出一个Client类。
具有:
import * as MyModule from 'my-module';
let client: MyModule.Client = new MyModule.Client();
编译失败,错误为:
Could not find a declaration file for module 'my-module'.
'[...]/node_modules/my-module/lib/index.js' implicitly has an 'any' type.
Try `npm install @types/my-module` if it exists or add a new declaration (.d.ts) file containing `declare module 'my-module';`
@types/my模块不存在,所以我在导入我的模块的文件旁边添加了一个my-module.d.ts文件,并添加了建议的行。然后我得到了错误:
Namespace '"my-module"' has no exported member 'Client'.
如果我在js应用程序中使用它,客户端实际上是导出的,并且正常工作。此外,前面的消息告诉我编译器正在查找正确的文件(/node_modules/my-module/lib/index.js是在my-modle/package.json“main”元素中定义的)。
我通过告诉编译器我不关心隐式的任何问题来解决这个问题,也就是说,我将tsconfig.json文件的以下行设置为false:
"noImplicitAny": false,
在我看来,解决这个问题的三种不同方法都不起作用。一旦在package.json中将“type”设置为“module”,那么它将符合ES module而不是CommonJS语法。我能够根据package.json设置使用ES模块语法来解决这个问题。
import ws from 'ws'
export const create = (/** @type {string} */ accessToken) => {
const WebSocket = ws;
return new WebSocket(endpoint, accessToken, sslOptions);
}
这样,您就可以在“ws”模块中使用WebSocket类。这是一个节点模块的示例,但基本上可以将任何类型的节点模块和函数放在其中。
下面这些对我不起作用:
npm安装-D@types/module名称const foo=require('模块名称');
// index.d.ts
declare module 'foo';
tsconfig.json中的配置
"noImplicitAny": true,
"allowJs": true