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将加载它在你的typeRoots
find的所有子文件夹。 如果你指定一个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.ts
files: []
属性。 -
在您的
files: []
包含./typings/index.d.ts
files: []
属性(然后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.ts
configuration指向的文件夹开始。
如果找不到文件,那么它会查找一个名为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/@types
searchnode_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
。 同时删除所有的///...
引用