TypeScript 2:无类型npm模块

时间:2016-10-24 15:29:47

标签: typescript npm typescript-typings

在尝试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"
    }
}

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

  1. 直接在foo.ts
  2. 中导入
  3. typings
  4. 中的package.json媒体资源
  5. typeRoots中使用tsconfig.json并使用文件typings/index.d.ts
  6. types
  7. 中使用明确的tsconfig.json
  8. types
  9. 中加入tsconfig.json目录
  10. 制作自定义typings.json文件并运行typings install
  11. 然而,当我运行Typescript时:

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

    我做错了什么?

4 个答案:

答案 0 :(得分:165)

不幸的是,这些东西目前还没有很好的记录,但即使你能够让它工作,让我们检查你的配置,以便你了解每个部分正在做什么以及它与打字稿过程的关系并加载打字。

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

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

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

关于typeRoots:[]types:[]属性的问题在于它们是 NOT 加载任意声明(*.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文件夹中的子文件夹,并尝试将它找到的每个子文件夹加载为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中找到的所有子文件夹。如果指定types:属性,则只会加载这些特定的子文件夹。

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

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

因此,让我们总结一下我们学到的东西。 Typescript 2.0引入了typeRootstypes来加载 npm packages 中打包的声明文件。如果您有自定义类型或单个松散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:

  5. 我们希望,在此基础上讨论你&#39;就可以知道为什么疯狂到您的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设置如何)。

    具体而言,明确导入模块始终绕过所有includesexcludesfilestypeRootstypes选项。所以当你这样做时:

    import {MyType} from 'my-module';
    

    完全忽略了上述所有属性。 模块解析期间的相关属性为baseUrlpathsmoduleResolution

    基本上,当使用node模块解析时,它会从{{指向的文件夹开始搜索文件名my-module.tsmy-module.tsxmy-module.d.ts 1}}配置。

    如果找不到该文件,那么它会查找名为baseUrl的文件夹,然后搜索my-module的{​​{1}}属性,如果有{ {1}}或者没有package.json属性,告诉它要加载哪个文件,然后在该文件夹中搜索typings

    如果仍未成功,则会从package.json开始在typings文件夹中搜索相同的内容。

    此外,如果找不到这些,它会搜索index.ts/tsx/d.ts所有相同的内容。

    如果它仍然没有找到任何内容,它将开始转到父目录并在那里搜索node_modulesbaseUrl/node_modules。它将继续上升到目录,直到它到达文件系统的根目录(甚至在项目外部获取节点模块)。

    我想强调的一点是,模块解析完全忽略了您设置的任何baseUrl/node_modules/@types。因此,如果您配置了node_modules,则在显式模块解析期间不会搜索到此内容。它只用作一个文件夹,您可以将整个应用程序提供的全局定义文件放在整个应用程序中,而无需进一步导入或引用。

    最后,您可以使用路径映射(即node_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' 模块。

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

    *

    这可以让你做到

    .ts

    然后从import

    中读取这些类型

答案 1 :(得分: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"
  ]
}

删除typings.json以及文件夹typings下的所有内容,lodash.d.ts除外。同时删除所有///...引用

答案 2 :(得分:1)

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

{
  "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.ts 具有declare module 'third-party-lib';

注意:上面的配置是不完整的配置,只是为了了解其类型,路径,包含和排除的外观。

答案 3 :(得分:1)

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

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

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

参考: