不幸的是,这些东西目前没有很好的文档记录,但即使你能够让它工作,让我们回顾一下你的配置,以便你了解每个部分在做什么以及它与打字稿处理和加载打字的方式有何关系。
首先让我们回顾一下您收到的错误:
error TS2688: Cannot find type definition file for 'lodash'.
此错误实际上并非来自您的导入或引用,或者您尝试在 ts 文件中的任何位置使用 lodash。相反,它来自对如何使用 typeRoots 和 types 属性的误解,所以让我们更详细地了解这些。
typeRoots:[] 和 types:[] 属性的问题在于它们是不是加载任意声明 (*.d.ts) 文件的通用方法。
这两个属性与新的 TS 2.0 特性直接相关,该特性允许从 NPM 包 打包和加载类型声明。
理解这一点非常重要,这些仅适用于 NPM 格式的文件夹(即包含 package.json 或 index.d.ts 的文件夹)。
typeRoots 的默认值为:
{
"typeRoots" : ["node_modules/@types"]
}
默认情况下,这意味着 typescript 将进入 node_modules/@types 文件夹并尝试将它在其中找到的每个子文件夹加载为 npm 包。
请务必了解,如果文件夹没有类似 npm 包的结构,这将失败。
这就是您的情况,也是您最初错误的根源。
您已将 typeRoot 切换为:
{
"typeRoots" : ["./typings"]
}
这意味着 typescript 现在将扫描 ./typings 文件夹中的 子文件夹 并尝试将找到的每个子文件夹加载为 npm 模块。
假设您刚刚设置了typeRoots 指向./typings,但还没有设置任何types:[] 属性。您可能会看到以下错误:
error TS2688: Cannot find type definition file for 'custom'.
error TS2688: Cannot find type definition file for 'global'.
这是因为tsc 正在扫描您的./typings 文件夹并查找子文件夹custom 和global。然后它试图将这些解释为 npm 包类型类型,但这些文件夹中没有 index.d.ts 或 package.json,因此您会收到错误消息。
现在让我们谈谈您正在设置的types: ['lodash'] 属性。这是做什么的?默认情况下,typescript 将加载它在您的 typeRoots 中找到的 all 子文件夹。如果您指定 types: 属性,它将仅加载那些特定的子文件夹。
在您的情况下,您告诉它加载 ./typings/lodash 文件夹,但它不存在。这就是为什么你会得到:
error TS2688: Cannot find type definition file for 'lodash'
让我们总结一下我们学到的东西。 Typescript 2.0 引入了 typeRoots 和 types 用于加载打包在 npm 包 中的声明文件。如果您有自定义类型或单个松散的 d.ts 文件,这些文件不包含在遵循 npm 包约定的文件夹中,那么这两个新属性不是您想要使用的。 Typescript 2.0 并没有真正改变这些内容的使用方式。您只需以多种标准方式之一将这些文件包含在编译上下文中:
直接将其包含在.ts 文件中:
///<reference path="../typings/custom/lodash.d.ts" />
在您的 files: [] 属性中包含 ./typings/custom/lodash.d.ts。
在您的 files: [] 属性中包含 ./typings/index.d.ts(然后递归包含其他类型。
将./typings/** 添加到您的includes:
希望,根据本次讨论,您将能够说出您对 tsconfig.json 的疯狂更改为何使事情再次起作用。
编辑:
我忘记提及的一件事是typeRoots 和types 属性实际上只对自动 加载全局声明有用。
例如,如果你
npm install @types/jquery
如果您使用的是默认的 tsconfig,那么该 jquery 类型包将自动加载,$ 将在您的所有脚本中可用,而无需进一步执行 ///<reference/> 或 import
typeRoots:[] 属性用于添加额外的位置,类型 packages 将从这些位置自动加载。
types:[] 属性的主要用例是禁用自动加载行为(通过将其设置为空数组),然后仅列出您希望全局包含的特定类型。
从各种typeRoots 加载类型包的另一种方法是使用新的///<reference types="jquery" /> 指令。注意types 而不是path。同样,这仅对全局声明文件有用,通常是那些不做import/export 的文件。
现在,这是导致与 typeRoots 混淆的原因之一。请记住,我说过typeRoots 是关于模块的全局包含。但@types/folder 也涉及标准模块分辨率(无论您的typeRoots 设置如何)。
具体来说,显式导入模块总是绕过所有includes、excludes、files、typeRoots 和types 选项。所以当你这样做时:
import {MyType} from 'my-module';
上面提到的所有属性都被完全忽略了。 模块解析期间的相关属性为baseUrl、paths和moduleResolution。
基本上,当使用node模块解析时,它会从baseUrl配置指向的文件夹开始搜索文件名my-module.ts、my-module.tsx、my-module.d.ts。
如果没有找到该文件,那么它将查找名为my-module 的文件夹,然后搜索具有typings 属性的package.json,如果有package.json 或没有typings 属性里面告诉它要加载哪个文件,然后在该文件夹中搜索index.ts/tsx/d.ts。
如果仍然不成功,它将在从您的baseUrl/node_modules 开始的node_modules 文件夹中搜索这些相同的内容。
此外,如果它没有找到这些,它将搜索baseUrl/node_modules/@types 以查找所有相同的东西。
如果它仍然没有找到任何东西,它将开始转到父目录并在那里搜索node_modules 和node_modules/@types。它将继续向上目录,直到它到达您的文件系统的根目录(甚至在您的项目之外获取节点模块)。
我要强调的一点是,模块解析完全忽略了您设置的任何typeRoots。因此,如果您配置了typeRoots: ["./my-types"],则在显式模块解析期间将不会对其进行搜索。它仅用作一个文件夹,您可以在其中放置您想要提供给整个应用程序的全局定义文件,而无需进一步导入或引用。
最后,您可以使用路径映射(即paths 属性)覆盖模块行为。例如,我提到在尝试解析模块时不会咨询任何自定义typeRoots。但如果你喜欢,你可以让这种行为发生:
"paths" :{
"*": ["my-custom-types/*", "*"]
}
这对所有匹配左侧的导入,在尝试包含它之前尝试修改右侧的导入(右侧的* 表示您的初始导入字符串。例如如果你导入:
import {MyType} from 'my-types';
它会首先尝试导入,就像你写的一样:
import {MyType} from 'my-custom-types/my-types'
然后如果它没有找到它会在没有前缀的情况下重试(数组中的第二项只是*,这意味着初始导入。
因此,通过这种方式,您可以添加其他文件夹来搜索自定义声明文件,甚至是您希望能够搜索到 import 的自定义 .ts 模块。
您还可以为特定模块创建自定义映射:
"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读取这些类型