首页 文章

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

提问于
浏览
63

在尝试在other places中发布的建议后,我发现自己无法运行使用无类型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');

文件 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 中使用显式 types

  • tsconfig.json 中包含 types 目录

  • 制作自定义 typings.json 文件并运行 typings install

然而,当我运行Typescript时:

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

我究竟做错了什么?

2 回答

  • 4

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

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

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

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

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

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

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

    typeRoots 的默认值为:

    {
       "typeRoots" : ["node_modules/@types"]
    }
    

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

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

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

    您已将typeRoot切换为:

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

    这意味着typescript现在将扫描 ./typings 文件夹以查找 subfolders 并尝试将它找到的每个子文件夹加载为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并没有真正改变这些消费方式 . 您只需使用以下标准方法之一将这些文件包含在编译上下文中:

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

    • files: [] 属性中包含 ./typings/custom/lodash.d.ts .

    • files: [] 属性中包含 ./typings/index.d.ts (然后递归地包括其他类型 .

    • ./typings/** 添加到 includes:

    希望在此讨论的基础上,您将能够告诉为什么您所做的更改让您的事情再次发挥作用 .

    编辑:

    我忘了提到的一件事是 typeRootstypes 属性实际上只对 automatic 加载全局声明有用 .

    例如,如果你

    npm install @types/jquery
    

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

    typeRoots:[] 属性用于添加自动加载类型包的其他位置 .

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

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

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

    具体而言,显式导入模块始终绕过所有 includes ,_ excludesfilestypeRootstypes 选项 . 所以当你这样做时:

    import {MyType} from 'my-module';
    

    所有上述属性都被完全忽略 . module resolution 期间的相关属性为 baseUrl ,_ pathsmoduleResolution .

    基本上,当使用 node 模块分辨率时,它将从 baseUrl 配置指向的文件夹开始搜索文件名 my-module.ts ,_ my-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: ["./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 读取这些类型

  • 131

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

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

相关问题