ChatGPT解决这个技术问题 Extra ChatGPT

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

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

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

文件夹结构

node_modules lodash

罗达什

src 脚.ts

脚.ts

打字自定义 lodash.d.ts 全局 index.d.ts

自定义 lodash.d.ts

lodash.d.ts

全球的

索引.d.ts

包.json

tsconfig.json

打字.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"
    }
}

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

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

然而,当我运行 Typescript 时:

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

我究竟做错了什么?


J
Jed Fox

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

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

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

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

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 文件夹中的 subfolders 并尝试将它找到的每个子文件夹加载为 npm 模块。

因此,假设您刚刚设置了指向 ./typingstypeRoots,但还没有设置任何 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 中找到的 所有 个子文件夹。如果您指定 types: 属性,它将仅加载那些特定的子文件夹。

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

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

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

直接将其包含在 .ts 文件中:/// 在您的文件中包含 ./typings/custom/lodash.d.ts:[]财产。在您的文件中包含 ./typings/index.d.ts: [] 属性(然后递归地包含其他类型。将 ./typings/** 添加到您的包含中:

希望根据本次讨论,您能够了解为什么您对 tsconfig.json 进行的疯狂更改会使事情再次发挥作用。

编辑:

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

例如,如果你

npm install @types/jquery

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

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

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

从各种 typeRoots 加载类型包的另一种方法是使用新的 ///<reference types="jquery" /> 指令。注意 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"] } 吗?
J
Jodiug

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

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


U
Uday Sravan K

"*": ["./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
< em>index.d.ts 有 declare module 'third-party-lib';

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


r
realharry

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

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

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

参考:

https://www.typescriptlang.org/docs/handbook/tsconfig-json.html