繁体   English   中英

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

[英]TypeScript 2: custom typings for untyped npm module

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

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

文件夹结构

  • 节点模块
    • 洛达什
  • 源文件
  • 打字
    • 定制
      • lodash.d.ts
    • 全球
    • 索引.d.ts
  • 包.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"
    }
}

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

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

然而,当我运行打字稿时:

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

我做错了什么?

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

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

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"]
}

默认情况下,这意味着打字稿将进入node_modules/@types文件夹并尝试加载它在那里找到的每个子文件夹作为npm package

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

这就是您的情况发生的情况,也是您初始错误的根源。

您已将 typeRoot 切换为:

{
    "typeRoots" : ["./typings"]
}

这意味着打字稿现在将扫描./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']属性。 这有什么作用? 默认情况下,打字稿将加载它在typeRoots找到的所有子文件夹。 如果你指定一个types:属性,它只会加载那些特定的子文件夹。

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

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

所以让我们总结一下我们学到的东西。 Typescript 2.0 引入了typeRoots和用于加载打包在npm 包中的声明文件的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: []属性(然后递归地包含其他类型。

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

我们希望,在此基础上讨论,你就可以告诉为什么改变你疯了你tsconfig.json让事情再次合作。

编辑:

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

例如,如果你

npm install @types/jquery

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

typeRoots:[]属性是为了添加额外的位置,从那里自动加载类型

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

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

现在,这是导致与typeRoots混淆的一件事。 记住,我说过typeRoots是关于模块的全局包含。 @types/folder也参与标准模块解析(无论您的typeRoots设置如何)。

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

import {MyType} from 'my-module';

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

基本上,当使用node模块的分辨率,它会开始寻找一个文件名my-module.tsmy-module.tsxmy-module.d.ts开始在文件夹由您指出baseUrl配置。

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

如果仍然不成功,它将在node_modules文件夹中搜索这些相同的内容,从您的node_modules baseUrl/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读取这些类型

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

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

"*": ["./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 旁边,即在客户端文件夹(或src文件夹)的级别中types/third-party-lib/index.d.ts
index.d.tsdeclare module 'third-party-lib';

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

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

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

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

参考:

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM