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的方法:

  1. 通过直接在foo.ts导入它
  2. 通过package.json属性
  3. 通过在文件typings/index.d.ts使用typeRoots中的typings/index.d.ts
  4. 通过在tsconfig.json使用显式types
  5. 通过在tsconfig.json包含types目录
  6. 通过定制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。 相反,它来自于对如何使用typeRootstypes属性的误解,所以让我们来详细介绍一下这些。

关于typeRoots:[]types:[]属性的一点是它们不是加载任意声明( *.d.ts )文件的通用方法。

这两个属性与新的TS 2.0function直接相关,它允许从NPM包中打包和加载types声明。

这是非常重要的理解,这些工作只与NPM格式的文​​件夹(即包含package.jsonindex.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文件夹,并findcustomglobal的子文件夹。 然后,它试图将这些解释为npm包types的键入,但是在这些文件夹中没有index.d.tspackage.json ,所以你得到错误。

现在让我们来谈谈一下你正在设置的types: ['lodash']属性。 这是做什么的? 缺省情况下,typescript将加载它在你的typeRootsfind的所有子文件夹。 如果你指定一个types:属性,它只会加载这些特定的子文件夹。

在你的情况下,你告诉它加载./typings/lodash文件夹,但它不存在。 这就是为什么你会得到:

 error TS2688: Cannot find type definition file for 'lodash' 

所以让我们总结一下我们所学到的东西。 Typescript 2.0引入了用于加载包装在npm包中的声明文件的typeRootstypes 。 如果你有自定义的types或单个松散的d.ts文件不包含npm包约定下的文件夹,那么这两个新的属性不是你想要使用的。 Typescript 2.0并没有真正改变这些消耗的方式。 您只需要以许多标准方式之一将这些文件包含在编译上下文中:

  1. 直接将其包含在.ts文件中: ///<reference path="../typings/custom/lodash.d.ts" />

  2. 在您的files: []包含./typings/custom/lodash.d.ts files: []属性。

  3. 在您的files: []包含./typings/index.d.ts files: []属性(然后recursion地包含其他types。

  4. ./typings/**添加到您的includes:

希望基于这个讨论,你将能够告诉为什么你对tsconfig.json的改变让事情再次起作用。

编辑:

有一件事我忘记了, typeRootstypes属性实际上只对自动加载全局声明有用。

例如,如果你

 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设置如何)。

具体而言,显式导入模块总是绕过所有includesexcludesfilestypeRootstypes选项。 所以当你这样做时:

 import {MyType} from 'my-module'; 

所有上述的属性都被完全忽略。 模块parsing期间的相关属性是baseUrlpathsmoduleResolution

基本上,使用node模块parsing时,它将开始search文件名my-module.tsmy-module.tsxmy-module.d.tsmy-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_modulesnode_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 。 同时删除所有的///...引用