TypeScript 2:无类型 npm 模块的自定义类型 [英] TypeScript 2: custom typings for untyped npm module

查看:40
本文介绍了TypeScript 2:无类型 npm 模块的自定义类型的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

After trying suggestions posted in other places, I find myself unable to get a typescript project running that uses an untyped NPM module. Below is a minimal example and the steps that I tried.

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

For this minimal example, we will pretend that lodash does not have existing type definitions. As such, we will ignore the package @types/lodash and try to manually add its typings file lodash.d.ts to our project.

文件夹结构

  • node_modules
    • lodash
    • 脚.ts
    • 自定义
      • lodash.d.ts

      接下来是文件.

      文件 foo.ts

      ///<reference path="../typings/custom/lodash.d.ts" />
      import * as lodash from 'lodash';
      
      console.log('Weeee');
      

      文件 lodash.d.ts 直接从原始 @types/lodash 包中复制.

      File lodash.d.ts is copied directly from the original @types/lodash package.

      文件 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"
          }
      }
      

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

      As you can see, I have tried many different ways of importing typings:

      1. 通过在foo.ts
      2. 中直接导入
      3. 通过 package.json 中的 typings 属性
      4. 通过在 tsconfig.json 中使用 typeRoots 和文件 typings/index.d.ts
      5. 通过在 tsconfig.json
      6. 中使用显式的 types
      7. 通过在 tsconfig.json
      8. 中包含 types 目录
      9. 制作自定义typings.json 文件并运行typings install
      1. By directly importing it in foo.ts
      2. By a typings property in package.json
      3. By using typeRoots in tsconfig.json with a file typings/index.d.ts
      4. By using an explicit types in tsconfig.json
      5. By including the types directory in tsconfig.json
      6. By making a custom typings.json file and running typings install

      然而,当我运行 Typescript 时:

      Yet, when I run Typescript:

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

      我做错了什么?

      推荐答案

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

      Unfortunately these things are not currently very well documented, but even if you were able to get it working, let's go over your configuration so that you understand what each part is doing and how it relates to how typescript processes and loads typings.

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

      First let's go over the error you are receiving:

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

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

      This error is actually not coming from your imports or references or your attempt to use lodash anywhere in your ts files. Rather it is coming from a misunderstanding of how to use the typeRoots and types properties, so let's go into a little more detail on those.

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

      The thing about typeRoots:[] and types:[] properties is that they are NOT general-purpose ways to load arbitrary declaration (*.d.ts) files.

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

      These two properties are directly related to the new TS 2.0 feature which allows packaging and loading typing declarations from NPM packages.

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

      This is very important to understand, that these work only with folders in NPM format (i.e. a folder containing a package.json or index.d.ts).

      typeRoots 的默认值是:

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

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

      By default this means that typescript will go into the node_modules/@types folder and try to load every sub-folder it finds there as a npm package.

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

      It is important to understand that this will fail if a folder does not have an npm package-like structure.

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

      This is what is happening in your case, and the source of your initial error.

      您已将 typeRoot 切换为:

      You have switched typeRoot to be:

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

      这意味着打字稿现在将扫描 ./typings 文件夹中的子文件夹,并尝试将它找到的每个子文件夹作为 npm 模块加载.

      This means that typescript will now scan the ./typings folder for subfolders and try to load each subfolder it finds as an npm module.

      所以让我们假设您刚刚设置了 typeRoots 指向 ./typings 但还没有任何 types:[] 属性设置.您可能会看到以下错误:

      So let's pretend you just had typeRoots setup to point to ./typings but did not yet have any types:[] property setup. You would likely see these errors:

      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 ,因此您收到错误.

      This is because tsc is scanning your ./typings folder and finding the sub-folders custom and global. It then is trying to interpret these as npm package-type typing, but there is no index.d.ts or package.json in these folders and so you get the error.

      现在让我们谈谈您正在设置的 types: ['lodash'] 属性.这有什么作用?默认情况下,打字稿将加载它在您的 typeRoots 中找到的所有子文件夹.如果您指定 types: 属性,它只会加载那些特定的子文件夹.

      Now let's talk a bit about the types: ['lodash'] property you are setting. What does this do? By default, typescript will load all sub-folders it finds within your typeRoots. If you specify a types: property it will only load those specific sub-folders.

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

      In your case you are telling it to load the ./typings/lodash folder but it doesn't exist. This is why you get:

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

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

      So let's summarize what we've learned. Typescript 2.0 introduced typeRoots and types for loading declaration files packaged in npm packages. If you have custom typings or single loose d.ts files that are not contained withing a folder following npm package conventions, then these two new properties are not what you want to use. Typescript 2.0 does not really change how these would be consumed. You just have to include these files in your compilation context in one of the many standard ways:

      1. 直接将其包含在 .ts 文件中:///

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

      Including ./typings/custom/lodash.d.ts in your files: [] property.

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

      Including ./typings/index.d.ts in your files: [] property (which then recursively includes the other typings.

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

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

      Hopefully, based on this discussion you'll be able to tell why the changes you mad to your tsconfig.json made things work again.

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

      One thing that I forgot to mention is that typeRoots and types property are really only useful for the automatic loading of global declarations.

      例如如果你

      npm install @types/jquery
      

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

      And you are using the default tsconfig, then that jquery types package will be loaded automatically and $ will be available throughout all your scripts wihtout having to do any further ///<reference/> or import

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

      The typeRoots:[] property is meant to add additional locations from where type packages will be loaded frrom automatically.

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

      The types:[] property's primary use-case is to disable the automatic loading behavior (by setting it to an empty array), and then only listing specific types you want to include globally.

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

      The other way to load type packages from the various typeRoots is to use the new ///<reference types="jquery" /> directive. Notice the types instead of path. Again, this is only useful for global declaration files, typically ones that don't do import/export.

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

      Now, here's one of the things that causes confusion with typeRoots. Remember, I said that typeRoots is about the global inclusion of modules. But @types/folder is also involved in standard module-resolution (regardless of your typeRoots setting).

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

      Specifically, explicitly importing modules always bypasses all includes, excludes, files, typeRoots and types options. So when you do:

      import {MyType} from 'my-module';
      

      上述所有属性都被完全忽略.模块解析的相关属性是baseUrlpathsmoduleResolution.

      All the above mentioned properties are completely ignored. The relevant properties during module resolution are baseUrl, paths, and moduleResolution.

      基本上,当使用node模块解析时,它会开始搜索一个文件名my-module.tsmy-module.tsx, my-module.d.ts 从您的 baseUrl 配置指向的文件夹开始.

      Basically, when using node module resolution, it will start searching for a file name my-module.ts, my-module.tsx, my-module.d.ts starting at the folder pointed to by your baseUrl configuration.

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

      If it doesn't find the file, then it will look for a folder named my-module and then search for a package.json with a typings property, if there is package.json or no typings property inside telling it which file to load it will then search for index.ts/tsx/d.ts within that folder.

      如果仍然不成功,它将在 node_modules 文件夹中搜索这些相同的内容,从您的 baseUrl/node_modules 开始.

      If that's still not successful it will search for these same things in the node_modules folder starting at your baseUrl/node_modules.

      另外,如果没有找到这些,它会在baseUrl/node_modules/@types中搜索所有相同的东西.

      In addition, if it doesn't find these, it will search baseUrl/node_modules/@types for all the same things.

      如果它仍然没有找到任何东西,它将开始转到父目录并在那里搜索 node_modulesnode_modules/@types.它将继续向上目录,直到到达文件系统的根目录(甚至在项目之外获取节点模块).

      If it still didn't find anything it will start going to the parent directory and search node_modules and node_modules/@types there. It will keep going up the directories until it reaches the root of your file system (even getting node-modules outside your project).

      我想强调的一件事是模块解析完全忽略您设置的任何 typeRoots.因此,如果您配置了 typeRoots: ["./my-types"],则不会在显式模块解析期间搜索.它仅用作一个文件夹,您可以在其中放置要提供给整个应用程序的全局定义文件,而无需进一步导入或引用.

      One thing I want to emphasize is that module resolution completely ignores any typeRoots you set. So if you configured typeRoots: ["./my-types"], this will not get searched during explicit module resolution. It only serves as a folder where you can put global definition files you want to make available to the whole application without further need to import or reference.

      最后,您可以使用路径映射(即 paths 属性)覆盖模块行为.例如,我提到在尝试解析模块时不会咨询任何自定义 typeRoots.但是,如果您愿意,您可以使这种行为发生:

      Lastly, you can override the module behavior with path mappings (i.e. the paths property). So for example, I mentioned that any custom typeRoots is not consulted when trying to resolve a module. But if you liked you can make this behavior happen as so:

      "paths" :{
           "*": ["my-custom-types/*", "*"]
       }
      

      这是针对与左侧匹配的所有导入,在尝试包含它之前尝试修改右侧的导入(右侧的 * 代表您的初始导入字符串.例如,如果您导入:

      What this does is for all imports that match the left-hand side, try modifying the import as in the right side before trying to include it (the * on the right hand side represents your initial import string. For example if you import:

      import {MyType} from 'my-types';
      

      它会首先尝试导入,就像你写的一样:

      It would first try the import as if you had written:

      import {MyType} from 'my-custom-types/my-types'
      

      然后如果它没有找到它会在没有前缀的情况下重试(数组中的第二项只是 * ,这意味着初始导入.

      And then if it didn't find it would try again wihtout the prefix (second item in the array is just * which means the initial import.

      因此,您可以通过这种方式添加其他文件夹来搜索您希望能够import 的自定义声明文件甚至自定义.ts 模块.

      So this way you can add additional folders to search for custom declaration files or even custom .ts modules that you want to be able to import.

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

      You can also create custom mappings for specific modules:

      "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

      这篇关于TypeScript 2:无类型 npm 模块的自定义类型的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆