TypeScript 2:非typesnpm模块的自定义types
在尝试了其他地方张贴的build议之后,我发现自己无法获得正在运行的使用无types的NPM模块的打字稿项目。 下面是一个简单的例子和我尝试的步骤。
对于这个最小的例子,我们将假装lodash没有现有的types定义。 因此,我们将忽略@types/lodash包,并尝试手动将其@types/lodash文件lodash.d.ts添加到我们的项目中。
文件夹结构
- node_modules
- lodash
- SRC
- foo.ts
- 分型
- 习惯
- lodash.d.ts
- 全球
- index.d.ts
- 习惯
- 的package.json
- tsconfig.json
- typings.json
接下来,这些文件。
文件foo.ts
///<reference path="../typings/custom/lodash.d.ts" /> import * as lodash from 'lodash'; console.log('Weeee');
文件lodash.d.ts直接从原始的@types/lodash包中复制。
文件index.d.ts
/// <reference path="custom/lodash.d.ts" /> /// <reference path="globals/lodash/index.d.ts" />
文件package.json
{ "name": "ts", "version": "1.0.0", "description": "", "main": "index.js", "typings": "./typings/index.d.ts", "dependencies": { "lodash": "^4.16.4" }, "author": "", "license": "ISC" }
文件tsconfig.json
{ "compilerOptions": { "target": "ES6", "jsx": "react", "module": "commonjs", "sourceMap": true, "noImplicitAny": true, "experimentalDecorators": true, "typeRoots" : ["./typings"], "types": ["lodash"] }, "include": [ "typings/**/*", "src/**/*" ], "exclude": [ "node_modules", "**/*.spec.ts" ] }
文件typings.json
{ "name": "TestName", "version": false, "globalDependencies": { "lodash": "file:typings/custom/lodash.d.ts" } }
正如你所看到的,我尝试了许多不同的导入types的方法:
- 通过直接在
foo.ts导入它 - 通过
package.json属性 - 通过在文件
typings/index.d.ts使用typeRoots中的typings/index.d.ts - 通过在
tsconfig.json使用显式types - 通过在
tsconfig.json包含types目录 - 通过定制
typings.json文件并运行typestypings install
然而,当我运行Typescript:
E:\temp\ts>tsc error TS2688: Cannot find type definition file for 'lodash'.
我究竟做错了什么?
不幸的是,这些东西目前还没有很好的logging,但即使你能够得到它的工作,让我们来看看你的configuration,以便了解每个部分正在做什么,以及它如何与打字稿处理和加载types。
首先让我们来看看你收到的错误:
error TS2688: Cannot find type definition file for 'lodash'.
这个错误实际上不是来自你的导入或引用,或者是你尝试在ts文件中的任何地方使用lodash。 相反,它来自于对如何使用typeRoots和types属性的误解,所以让我们来详细介绍一下这些。
关于typeRoots:[]和types:[]属性的一点是它们不是加载任意声明( *.d.ts )文件的通用方法。
这两个属性与新的TS 2.0function直接相关,它允许从NPM包中打包和加载types声明。
这是非常重要的理解,这些工作只与NPM格式的文件夹(即包含package.json或index.d.ts的文件夹)。
typeRoots的默认值是:
{ "typeRoots" : ["node_modules/@types"] }
默认情况下,这意味着打字稿将进入node_modules/@types文件夹,并尝试将其find的每个子文件夹加载为npm包 。
理解这一点非常重要,如果一个文件夹没有npm包结构,这将会失败。
这是你的情况正在发生的事情,以及你最初的错误的来源。
您已将typeRoot切换为:
{ "typeRoots" : ["./typings"] }
这意味着打字机现在将扫描子文件夹的./typings文件夹,并尝试加载它作为npm模块find的每个子文件夹。
所以让我们假装你刚刚将typeRoots设置为指向./typings但是还没有任何types:[]属性设置。 你可能会看到这些错误:
error TS2688: Cannot find type definition file for 'custom'. error TS2688: Cannot find type definition file for 'global'.
这是因为./typings正在扫描您的./typings文件夹,并findcustom和global的子文件夹。 然后,它试图将这些解释为npm包types的键入,但是在这些文件夹中没有index.d.ts或package.json ,所以你得到错误。
现在让我们来谈谈一下你正在设置的types: ['lodash']属性。 这是做什么的? 缺省情况下,typescript将加载它在你的typeRootsfind的所有子文件夹。 如果你指定一个types:属性,它只会加载这些特定的子文件夹。
在你的情况下,你告诉它加载./typings/lodash文件夹,但它不存在。 这就是为什么你会得到:
error TS2688: Cannot find type definition file for 'lodash'
所以让我们总结一下我们所学到的东西。 Typescript 2.0引入了用于加载包装在npm包中的声明文件的typeRoots和types 。 如果你有自定义的types或单个松散的d.ts文件不包含npm包约定下的文件夹,那么这两个新的属性不是你想要使用的。 Typescript 2.0并没有真正改变这些消耗的方式。 您只需要以许多标准方式之一将这些文件包含在编译上下文中:
-
直接将其包含在
.ts文件中:///<reference path="../typings/custom/lodash.d.ts" /> -
在您的
files: []包含./typings/custom/lodash.d.tsfiles: []属性。 -
在您的
files: []包含./typings/index.d.tsfiles: []属性(然后recursion地包含其他types。 -
将
./typings/**添加到您的includes:
希望基于这个讨论,你将能够告诉为什么你对tsconfig.json的改变让事情再次起作用。
编辑:
有一件事我忘记了, typeRoots和types属性实际上只对自动加载全局声明有用。
例如,如果你
npm install @types/jquery
而且你使用的是默认的tsconfig,那么jquerytypes包会自动加载,并且在你所有的脚本中都可以使用$ ,而不需要做进一步的处理///<reference/>或import
typeRoots:[]属性是为了从types包自动加载的地方添加额外的位置。
types:[]属性的主要用例是禁用自动加载行为(通过将其设置为空数组),然后仅列出要全局包含的特定types。
另一种从各种types的typeRoots加载types包的typeRoots是使用新的///<reference types="jquery" />指令。 注意types而不是path 。 同样,这只对全局声明文件有用,通常是不import/export 。
现在,这是导致与typeRoots混淆的事情typeRoots 。 请记住,我说的typeRoots是关于模块的全局包含。 但@types/folder也包含在标准模块分辨率中(不pipe你的typeRoots设置如何)。
具体而言,显式导入模块总是绕过所有includes , excludes , files , typeRoots和types选项。 所以当你这样做时:
import {MyType} from 'my-module';
所有上述的属性都被完全忽略。 模块parsing期间的相关属性是baseUrl , paths和moduleResolution 。
基本上,使用node模块parsing时,它将开始search文件名my-module.ts , my-module.tsx , my-module.d.ts从my-module.d.tsconfiguration指向的文件夹开始。
如果找不到文件,那么它会查找一个名为my-module的文件夹,然后search带有typings属性的package.json ,如果有package.json或no typings属性告诉它加载哪个文件它会在那个文件夹中searchindex.ts/tsx/d.ts
如果仍然不成功,它会从node_modules baseUrl/node_modules开始在node_modules文件夹中search这些相同的东西。
另外,如果没有find这些,它将searchbaseUrl/node_modules/@types的所有相同的东西。
如果仍然没有find任何东西,它将开始进入父目录,并在node_modules/@typessearchnode_modules和node_modules/@types 。 它将继续上升目录,直到到达文件系统的根目录(即使是在你的项目之外获得节点模块)。
我想强调的一件事是模块分辨率完全忽略了你设置的任何types的typeRoots 。 因此,如果您configuration了typeRoots: ["./my-types"] ,则在明确的模块parsing期间将不会执行search。 它只能作为一个文件夹,你可以把全局定义文件放到整个应用程序中,而不需要导入或引用。
最后,你可以用path映射(即paths属性)覆盖模块行为。 所以举个例子,我提到在试图parsing一个模块的时候没有查阅任何自定义types的typeRoots 。 但如果你喜欢,你可以使这种行为发生如此:
"paths" :{ "*": ["my-custom-types/*", "*"] }
对于所有与左侧相匹配的导入,这个function是做的,在试图包含它的导入之前,尝试修改导入(右边的*表示你的初始导入string。例如,如果导入:
import {MyType} from 'my-types';
它会首先尝试导入,就像你写了:
import {MyType} from 'my-custom-types/my-types'
然后,如果它没有find它会尝试再次没有前缀(数组中的第二项只是*这意味着最初的导入。
这样,您可以添加额外的文件夹来search自定义声明文件,甚至是您想要import自定义.ts模块。
您也可以为特定模块创build自定义映射:
"paths" :{ "*": ["my-types", "some/custom/folder/location/my-awesome-types-file"] }
这会让你这样做
import {MyType} from 'my-types';
但是,然后从some/custom/folder/location/my-awesome-types-file.d.ts读取这些types
我仍然不明白这一点,但我find了一个解决scheme。 使用下面的tsconfig.json :
{ "compilerOptions": { "target": "ES6", "jsx": "react", "module": "commonjs", "sourceMap": true, "noImplicitAny": true, "experimentalDecorators": true, "baseUrl": ".", "paths": { "*": [ "./typings/*" ] } }, "include": [ "src/**/*" ], "exclude": [ "node_modules", "**/*.spec.ts" ] }
删除typings.json和文件夹下的所有东西,除了lodash.d.ts 。 同时删除所有的///...引用