TypeScript 2:无类型npm模块的自定义类型

Jod*_*iug 83 npm typescript typescript-typings

在尝试在其他地方发布的建议后,我发现自己无法运行使用无类型NPM模块的打字稿项目.下面是一个最小的例子和我尝试的步骤.

对于这个最小的例子,我们假装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');
Run Code Online (Sandbox Code Playgroud)

文件lodash.d.ts直接从原始@types/lodash包复制.

文件 index.d.ts

/// <reference path="custom/lodash.d.ts" />
/// <reference path="globals/lodash/index.d.ts" />
Run Code Online (Sandbox Code Playgroud)

文件 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"
}
Run Code Online (Sandbox Code Playgroud)

文件 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"
  ]
}
Run Code Online (Sandbox Code Playgroud)

文件 typings.json

{
    "name": "TestName",
    "version": false,
    "globalDependencies": {
        "lodash": "file:typings/custom/lodash.d.ts"
    }
}
Run Code Online (Sandbox Code Playgroud)

如您所见,我尝试了许多不同的导入类型的方法:

  1. 通过直接导入它 foo.ts
  2. 由一个typings财产package.json
  3. 通过使用typeRootsin tsconfig.json文件typings/index.d.ts
  4. 通过使用一个明确typestsconfig.json
  5. 通过包含types目录tsconfig.json
  6. 通过制作自定义typings.json文件并运行typings install

然而,当我运行Typescript时:

E:\temp\ts>tsc
error TS2688: Cannot find type definition file for 'lodash'.
Run Code Online (Sandbox Code Playgroud)

我究竟做错了什么?

dta*_*enc 182

不幸的是,这些东西目前还没有很好地记录,但即使你能够使它工作,让我们回顾一下你的配置,以便你了解每个部分正在做什么以及它与打字稿处理和加载类型的关系.

首先让我们回顾一下您收到的错误:

error TS2688: Cannot find type definition file for 'lodash'.
Run Code Online (Sandbox Code Playgroud)

此错误实际上不是来自您的导入或引用,也不是您尝试在ts文件中的任何位置使用lodash.相反,它来自对如何使用typeRootstypes属性的误解,所以让我们进一步详细介绍.

关于typeRoots:[]types:[]属性的事情是它们不是加载任意declaration(*.d.ts)文件的通用方法.

这两个属性与新的TS 2.0功能直接相关,它允许从NPM包中打包和加载类型声明.

理解这一点非常重要,这些只适用于NPM格式的文​​件夹(即包含package.jsonindex.d.ts的文件夹).

默认typeRoots值为:

{
   "typeRoots" : ["node_modules/@types"]
}
Run Code Online (Sandbox Code Playgroud)

默认情况下,这意味着typescript将进入该node_modules/@types文件夹并尝试将其在那里找到的每个子文件夹加载为npm包.

重要的是要理解,如果文件夹没有类似npm包的结构,这将失败.

这就是您的情况,以及您的初始错误的来源.

您已将typeRoot切换为:

{
    "typeRoots" : ["./typings"]
}
Run Code Online (Sandbox Code Playgroud)

这意味着typescript现在将扫描./typings文件夹中的子文件夹并尝试将它找到的每个子文件夹加载为npm模块.

所以,让我们假装你只是typeRoots设置指向,./typings但还没有任何types:[]属性设置.你可能会看到这些错误:

error TS2688: Cannot find type definition file for 'custom'.
error TS2688: Cannot find type definition file for 'global'.
Run Code Online (Sandbox Code Playgroud)

这是因为tsc正在扫描您的./typings文件夹并找到子文件夹customglobal.然后它试图将这些解释为npm包类型键入,但是没有index.d.tspackage.json在这些文件夹中,因此您得到错误.

现在让我们谈谈types: ['lodash']您正在设置的属性.这是做什么的?默认情况下,typescript将加载它在您的文件夹中找到的所有子文件夹typeRoots.如果指定types:属性,则只会加载这些特定的子文件夹.

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

error TS2688: Cannot find type definition file for 'lodash'
Run Code Online (Sandbox Code Playgroud)

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

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

  2. 包括./typings/custom/lodash.d.ts在您的files: []财产.

  3. 包括./typings/index.d.ts在你的files: []财产(然后递归包括其他类型.

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

希望在此讨论的基础上,您将能够分辨为什么您对您tsconfig.json所做的事情的变化再次起作用.

编辑:

有一件事我忘了提的是,typeRootstypes物业真的是只对有用的自动全局声明的负荷.

例如,如果你

npm install @types/jquery
Run Code Online (Sandbox Code Playgroud)

并且您正在使用默认的tsconfig,然后将自动加载jquery类型包,并且$可以在所有脚本中使用,而无需进一步执行///<reference/>import

typeRoots:[]属性旨在添加自动加载类型包的位置.

types:[]属性的主要用例是禁用自动加载行为(通过将其设置为空数组),然后仅列出要全局包含的特定类型.

从各种类型加载类型包的另一种方法typeRoots是使用new ///<reference types="jquery" />指令.注意types而不是path.同样,这仅对全局声明文件有用,通常是那些不起作用的文件import/export.

现在,这是引起混淆的事情之一typeRoots.请记住,我说这typeRoots是关于模块的全球包含.但@types/folder也涉及标准模块分辨率(无论您的typeRoots设置如何).

具体来说,明确导入模块总是绕过所有 includes,excludes,files,typeRootstypes选项.所以当你这样做时:

import {MyType} from 'my-module';
Run Code Online (Sandbox Code Playgroud)

所有上述属性都被完全忽略.在相关性模块的分辨率baseUrl,pathsmoduleResolution.

基本上,当使用node模块的分辨率,它将开始搜索文件名my-module.ts,my-module.tsx,my-module.d.ts开始在文件夹指向您的baseUrl配置.

如果它找不到该文件,那么它将查找一个名为的文件夹my-module然后搜索package.json带有typings属性的文件夹,如果内部有package.json或没有typings属性告诉它要加载哪个文件,那么它将index.ts/tsx/d.ts在该文件夹中搜索.

如果仍然没有成功,它会node_modules在你的文件夹中搜索这些相同的东西baseUrl/node_modules.

另外,如果找不到这些,它将搜索baseUrl/node_modules/@types所有相同的东西.

如果它仍然没有找到任何东西,它将开始进入父目录并搜索node_modulesnode_modules/@types在那里.它将继续上升到目录,直到它到达文件系统的根目录(甚至在项目外部获取节点模块).

我想强调的一件事是模块解析完全忽略了typeRoots你设置的任何东西.因此,如果您已配置typeRoots: ["./my-types"],则在显式模块解析期间不会搜索到此内容.它仅用作一个文件夹,您可以将整个应用程序提供的全局定义文件放在整个应用程序中,而无需进一步导入或引用.

最后,您可以使用路径映射(即paths属性)覆盖模块行为.例如,我提到typeRoots在尝试解析模块时不会查询任何自定义.但如果你喜欢你可以让这种行为发生如下:

"paths" :{
     "*": ["my-custom-types/*", "*"]
 }
Run Code Online (Sandbox Code Playgroud)

这样做对于与左侧匹配的所有导入,尝试在尝试包含它之前修改右侧的导入(*右侧表示您的初始导入字符串.例如,如果您导入:

import {MyType} from 'my-types';
Run Code Online (Sandbox Code Playgroud)

它会首先尝试导入,就像你写的那样:

import {MyType} from 'my-custom-types/my-types'
Run Code Online (Sandbox Code Playgroud)

然后,如果它没有找到它将再次尝试与前缀(数组中的第二项只是*这意味着初始导入.

因此,您可以通过这种方式添加其他文件夹来搜索自定义声明文件,甚至.ts是您希望能够使用的自定义模块import.

您还可以为特定模块创建自定义映射:

"paths" :{
   "*": ["my-types", "some/custom/folder/location/my-awesome-types-file"]
 }
Run Code Online (Sandbox Code Playgroud)

这会让你这样做

import {MyType} from 'my-types';
Run Code Online (Sandbox Code Playgroud)

但是从那里读取那些类型 some/custom/folder/location/my-awesome-types-file.d.ts

  • 应该有一个来自 http://www.typescriptlang.org/docs/handbook/tsconfig-json.html 的答案的链接 (3认同)
  • 感谢您的详细回答。事实证明,这些解决方案单独工作,但不能很好地混合。它确实澄清了事情,所以我会给你赏金。如果您能抽出时间回答更多问题,这可能对其他人很有帮助。(1) typeRoots 是否有理由只接受特定的文件夹结构?看起来很随意。(2) 如果我更改 typeRoots,typescript 仍会在 node_modules 中包含 @types 文件夹,这与规范相反。(3) 什么是“paths”?就类型而言,它与“include”有何不同? (2认同)
  • 我编辑了答案,如果您还有任何问题,请告诉我。 (2认同)
  • 谢谢你,我读了剩下的部分,哇。祝你找到这一切。看起来这里有很多不必要的复杂行为在起作用。我希望 Typescript 将来能够让它们的配置属性更加自我记录。 (2认同)
  • 最后一个例子不应该像`“ paths”:{“ my-types :: [” some / custom / folder / location / my-awesome-types-file“]}`一样吗? (2认同)

Jod*_*iug 6

编辑:过时.阅读上面的答案.

我仍然不明白这一点,但我找到了解决方案.使用以下内容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"
  ]
}
Run Code Online (Sandbox Code Playgroud)

删除除typings.json文件夹下的所有内容.同时删除所有引用typingslodash.d.ts///...