【问题标题】:TypeScript 2: custom typings for untyped npm moduleTypeScript 2:无类型 npm 模块的自定义类型
【发布时间】:2017-03-06 10:50:34
【问题描述】:

在尝试了other places 中发布的建议后,我发现自己无法运行使用无类型 NPM 模块的 typescript 项目。下面是一个最小的例子和我尝试过的步骤。

对于这个最小的例子,我们假设lodash 没有现有的类型定义。因此,我们将忽略包@types/lodash 并尝试手动将其类型文件lodash.d.ts 添加到我们的项目中。

文件夹结构

  • node_modules
    • lodash
    • 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"
    }
}

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

  1. 直接导入foo.ts
  2. 通过package.json 中的typings 属性
  3. 通过在tsconfig.json 中使用typeRoots 和文件typings/index.d.ts
  4. 通过在tsconfig.json 中使用显式types
  5. 通过在tsconfig.json 中包含types 目录
  6. 通过制作自定义typings.json 文件并运行typings install

然而,当我运行 Typescript 时:

E:\temp\ts>tsc
error TS2688: Cannot find type definition file for 'lodash'.

我做错了什么?

【问题讨论】:

    标签: typescript npm typescript-typings


    【解决方案1】:

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

    我仍然不明白这一点,但我找到了解决方案。 使用以下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 和文件夹typings 下的所有内容,lodash.d.ts 除外。同时删除所有///... 引用

    【讨论】:

      【解决方案2】:

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

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

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

      此错误实际上并非来自您的导入或引用,或者您尝试在 ts 文件中的任何位置使用 lodash。相反,它来自对如何使用 typeRootstypes 属性的误解,所以让我们更详细地了解这些。

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

      这两个属性与新的 TS 2.0 特性直接相关,该特性允许从 NPM 包 打包和加载类型声明。

      理解这一点非常重要,这些仅适用于 NPM 格式的文件夹(即包含 package.jsonindex.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 文件夹并查找子文件夹customglobal。然后它试图将这些解释为 npm 包类型类型,但这些文件夹中没有 index.d.tspackage.json,因此您会收到错误消息。

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

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

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

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

      1. 直接将其包含在.ts 文件中: ///&lt;reference path="../typings/custom/lodash.d.ts" /&gt;

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

      3. 在您的 files: [] 属性中包含 ./typings/index.d.ts(然后递归包含其他类型。

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

      希望,根据本次讨论,您将能够说出您对 tsconfig.json 的疯狂更改为何使事情再次起作用。

      编辑:

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

      例如,如果你

      npm install @types/jquery
      

      如果您使用的是默认的 tsconfig,那么该 jquery 类型包将自动加载,$ 将在您的所有脚本中可用,而无需进一步执行 ///&lt;reference/&gt;import

      typeRoots:[] 属性用于添加额外的位置,类型 packages 将从这些位置自动加载。

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

      从各种typeRoots 加载类型包的另一种方法是使用新的///&lt;reference types="jquery" /&gt; 指令。注意types 而不是path。同样,这仅对全局声明文件有用,通常是那些不做import/export 的文件。

      现在,这是导致与 typeRoots 混淆的原因之一。请记住,我说过typeRoots 是关于模块的全局包含。但@types/folder 也涉及标准模块分辨率(无论您的typeRoots 设置如何)。

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

      import {MyType} from 'my-module';
      

      上面提到的所有属性都被完全忽略了。 模块解析期间的相关属性为baseUrlpathsmoduleResolution

      基本上,当使用node模块解析时,它会从baseUrl配置指向的文件夹开始搜索文件名my-module.tsmy-module.tsxmy-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_modulesnode_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读取这些类型

      【讨论】:

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

      "*": ["./types/*"] tsconfig 路径中​​的这一行经过 2 小时的努力修复了所有问题。

      {
        "compilerOptions": {
          "moduleResolution": "node",
          "strict": true,
          "baseUrl": ".",
          "paths": {
            "*": ["./types/*"]
          },
          "jsx": "react",
          "types": ["node", "jest"]
        },
        "include": [
          "client/**/*",
          "packages/**/*"
        ],
        "exclude": [
          "node_modules/**/*"
        ]
      }
      

      types 是文件夹名称,位于 node_module 旁边,即在 client 文件夹(或 src 文件夹)的级别 types/third-party-lib/index.d.ts
      index.d.tsdeclare module 'third-party-lib';

      注意:上面的配置是一个不完整的配置,只是为了说明它的类型、路径、包含和排除在其中的样子。

      【讨论】:

        【解决方案4】:

        我知道这是一个老问题,但打字稿工具一直在不断变化。我认为此时最好的选择就是依赖 tsconfig.json 中的“包含”路径设置。

          "include": [
                "src/**/*"
            ],
        

        默认情况下,除非您进行特定更改,否则src/ 下的所有 *.ts 和所有 *.d.ts 文件都将自动包含在内。我认为这是在不自定义 typeRootstypes 的情况下包含自定义类型声明文件的最简单/最佳方式。

        参考:

        【讨论】:

          猜你喜欢
          • 2017-02-17
          • 2017-07-30
          • 2020-05-26
          • 2017-11-05
          • 2020-11-11
          • 1970-01-01
          • 1970-01-01
          • 2018-08-24
          • 1970-01-01
          相关资源
          最近更新 更多