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 。 同时删除所有的///...引用