【问题标题】:How do you produce a .d.ts "typings" definition file from an existing JavaScript library?如何从现有的 JavaScript 库生成 .d.ts “类型”定义文件?
【发布时间】:2012-09-23 03:48:01
【问题描述】:

我正在使用我自己和第三方的很多库。我看到“typings”目录包含一些用于 Jquery 和 WinRT...但它们是如何创建的?

【问题讨论】:

    标签: typescript tsc


    【解决方案1】:

    您可以使用几个选项,具体取决于相关库、它的编写方式以及您正在寻找的准确度级别。让我们按照期望的大致降序来查看这些选项。

    也许它已经存在

    始终首先检查 DefinitiveTyped (https://github.com/DefinitelyTyped/DefinitelyTyped)。这是一个包含数以千计的 .d.ts 文件的社区存储库,很可能您正在使用的东西已经存在。 您还应该检查 TypeSearch (https://microsoft.github.io/TypeSearch/),它是 NPM 发布的 .d.ts 文件的搜索引擎;这将比DefiniteTyped 有更多的定义。 一些模块也将它们自己的定义作为其 NPM 分发的一部分提供,因此在尝试编写自己的模块之前还要看看是否是这种情况。

    也许你不需要一个

    TypeScript 现在支持 --allowJs 标志,并将在 .js 文件中进行更多基于 JS 的推理。您可以尝试在编译中包含 .js 文件以及 --allowJs 设置,看看这是否为您提供了足够好的类型信息。 TypeScript 可以识别这些文件中的 ES5 样式类和 JSDoc cmets,但如果库以一种奇怪的方式初始化自身,则可能会出错。

    开始使用--allowJs

    如果--allowJs 给了你不错的结果并且你想自己编写一个更好的定义文件,你可以结合--allowJs--declaration 来看看TypeScript 对库类型的“最佳猜测”。这将为您提供一个不错的起点,如果 JSDoc cmets 编写良好并且编译器能够找到它们,那么它可能与手写文件一样好。

    dts-gen 入门

    如果 --allowJs 不起作用,您可能需要使用 dts-gen (https://github.com/Microsoft/dts-gen) 来获得一个起点。此工具使用对象的运行时形状来准确枚举所有可用属性。从好的方面来说,这往往非常准确,但该工具还不支持抓取 JSDoc cmets 以填充其他类型。你这样运行:

    npm install -g dts-gen
    dts-gen -m <your-module>
    

    这将在当前文件夹中生成your-module.d.ts

    点击贪睡按钮

    如果你只是想稍后再做,暂时不用类型,现在可以在 TypeScript 2.0 中编写

    declare module "foo";
    

    这将让您 import 使用 any 类型的 "foo" 模块。如果你有一个全局的你想稍后处理,只需写

    declare const foo: any;
    

    这会给你一个foo 变量。

    【讨论】:

    • 哎呀...在许多情况下,这将成为进入的重要障碍。如果有一个工具可以根据类型推断至少输出“最佳猜测”,那就太好了。虽然这些可能不是最佳的,但至少可以随着时间的推移进行调整。
    • +1 - 更多好消息:--declarations 同时生成.js 文件和.d.ts 文件,这意味着您只需要运行一次编译。
    • 流行图书馆的高质量定义集合可以在github.com/borisyankov/DefinitelyTyped找到。
    • “最佳猜测”工具不会比 TypeScript 中已经存在的 IntelliSense 更好。类型定义的好处是为编译器提供了比它“猜测”更多的信息。
    • --allowJs--declaration 选项不能组合(在 TypeScript 1.8 和 2.0 中测试)。如果我尝试,我会得到:error TS5053: Option 'allowJs' cannot be specified with option 'declaration'
    【解决方案2】:

    您可以像 Ryan 描述的那样使用 tsc --declaration fileName.ts,或者您可以在 tsconfig.json 中的 compilerOptions 下指定 declaration: true,前提是您的项目下已经有 tsconfig.json

    【讨论】:

    • 如果你正在开发一个你想分享的 Angular 2 模块,这是最好的答案。
    • 如果我尝试tsc --declaration test.ts,我会收到错误Cannot find name...,因为我正在尝试为其创建声明文件:) 所以我需要这些类型才能声明它们?跨度>
    • @Kokodoko,您可以尝试将declaration: true 添加到您的tsconfig.json 文件中吗?
    • 谢谢,我只是想从 *.ts 生成 *.d.ts。
    【解决方案3】:

    处理这个问题的最好方法(如果声明文件在DefinitelyTyped 上不可用)是只为您使用的东西而不是整个库编写声明。这大大减少了工作量 - 此外,编译器可以通过抱怨缺少方法来提供帮助。

    【讨论】:

    • 不幸的是,文档毫无希望。
    【解决方案4】:

    正如 Ryan 所说,tsc 编译器有一个开关 --declaration,它从 .ts 文件生成 .d.ts 文件。另请注意(排除错误)TypeScript 应该能够编译 Javascript,因此您可以将现有的 javascript 代码传递给 tsc 编译器。

    【讨论】:

    • 似乎当前将 *.js 文件传递​​到 tsc 会生成错误“读取文件“angular-resource.js”时出错:找不到文件”。因此,您必须将 *.js 文件显式重命名为 *.ts 才能使用 tsc。有关更多详细信息,请参阅此问题 - 我尝试在 angularjs 上使用它:typescript.codeplex.com/workitem/26
    • 据我所知,tsc 可以编译任何有效的 JavaScript,但如果它不是有效的 TypeScript(编译给出警告),那么 --declarations 标志不会输出 .d.ts 文件,所以除非您的库是用 TypeScript 编写的,否则您仍然需要手动创建声明文件。
    【解决方案5】:

    创建自己的库时,可以使用tsc(TypeScript 编译器)命令创建*.d.ts 文件,如下所示: (假设您正在将库构建到 dist/lib 文件夹)

    tsc -d --declarationDir dist/lib --declarationMap --emitDeclarationOnly
    
    • -d (--declaration):生成*.d.ts文件
    • --declarationDir dist/lib:生成的声明文件的输出目录。
    • --declarationMap:为每个对应的“.d.ts”文件生成一个源映射。
    • --emitDeclarationOnly:只发出“.d.ts”声明文件。 (没有编译的 JS)

    (请参阅docs 了解所有命令行编译器选项)

    或者例如在你的package.json:

    "scripts": {
        "build:types": "tsc -d --declarationDir dist/lib --declarationMap --emitDeclarationOnly",
    }
    

    然后运行:yarn build:types(或npm run build:types

    【讨论】:

    • 我仍然无法弄清楚如何生成d.ts 文件并能够使用接口。你有例子吗?
    • 上面的 npm 脚本会生成 *.d.ts 文件并将它们放在 dist/lib 文件夹中。您确实需要在项目的根目录中有一个 tsconfig.json 文件,但无论如何项目都应该在那里工作。
    • 一旦暴露,我将无法使用它们。我有以下帖子stackoverflow.com/questions/59742688,如果你能让它工作?
    【解决方案6】:

    正如http://channel9.msdn.com/posts/Anders-Hejlsberg-Steve-Lucco-and-Luke-Hoban-Inside-TypeScript 00:33:52 中所述,他们构建了一个工具来将 WebIDL 和 WinRT 元数据转换为 TypeScript d.ts

    【讨论】:

      【解决方案7】:

      这里有一些 PowerShell,它创建了一个单一的 TypeScript 定义文件,一个包含多个 *.js 文件的库。

      首先,将所有扩展名更改为.ts

      Get-ChildItem | foreach { Rename-Item $_ $_.Name.Replace(".js", ".ts") }
      

      其次,使用 TypeScript 编译器生成定义文件。会有一堆编译器错误,但我们可以忽略它们。

      Get-ChildItem | foreach { tsc $_.Name  }
      

      最后,将所有*.d.ts 文件合并为一个index.d.ts,删除import 语句并从每个导出语句中删除default

      Remove-Item index.d.ts; 
      
      Get-ChildItem -Path *.d.ts -Exclude "Index.d.ts" | `
        foreach { Get-Content $_ } | `
        where { !$_.ToString().StartsWith("import") } | `
        foreach { $_.Replace("export default", "export") } | `
        foreach { Add-Content index.d.ts $_ }
      

      这以包含许多定义的单个可用index.d.ts 文件结束。

      【讨论】:

      • 第二步,Get-ChildItem | foreach { tsc --declaration $_.Name } 工作(添加缺少的 --declaration 参数)
      【解决方案8】:

      我会寻找支持 Script# 或 SharpKit 的 3rd 方 JS 库的现有映射。这些 C# 到 .js 交叉编译器的用户将面临您现在面临的问题,并且可能已经发布了一个开源程序来扫描您的 3rd 方库并转换为骨架 C# 类。如果是这样,请破解扫描程序以生成 TypeScript 来代替 C#。

      如果做不到这一点,将第 3 方库的 C# 公共接口转换为 TypeScript 定义可能比通过读取源 JavaScript 来做同样的事情更简单。

      我特别感兴趣的是 Sencha 的 ExtJS RIA 框架,我知道已经发布了一些项目来为 Script# 或 SharpKit 生成 C# 解释

      【讨论】:

      【解决方案9】:

      此答案显示了如何使用 typescript 编译器 API 在独立函数中执行此操作:

      import ts from 'typescript';
      function createDTsTextsFromJsFilePaths(
        jsFileNames: string[]
      ): Record<string, string> {
        const options = {
          allowJs: true,
          declaration: true,
          emitDeclarationOnly: true,
        };
        // Create a Program with an in-memory emit
        const createdFiles: Record<string, string> = {};
        const host = ts.createCompilerHost(options);
        host.writeFile = (fileName: string, contents: string): void => {
          const dtsName = fileName.replace('.js', '.d.ts');
          createdFiles[dtsName] = contents;
        };
        // Prepare and emit the d.ts files
        const program = ts.createProgram(jsFileNames, options, host);
        program.emit();
        return createdFiles;
      }
      ////////////// test code below here ///////////////////////////////////
      import fs from 'fs'
      const jsTest = 
      `
      "use strict"
      function x(){
        return {
          a: 1, 
          b: ()=>'hello'
        }
      }
      `;
      const jsPath = '/tmp/jstest.js';
      fs.writeFileSync(jsPath, jsTest);
      const createdFiles = createDTsTextsFromJsFilePaths([jsPath]);
      for (const dtsName in createdFiles) {
        console.log('================');
        console.log(dtsName);
        console.log('----------------');
        console.log(createdFiles[dtsName]);
      }
      

      执行测试给出结果

      ================
      /tmp/jstest.d.ts
      ----------------
      declare function x(): {
          a: number;
          b: () => string;
      };
      

      函数来源于documentation in the type script compiler API guide

      【讨论】:

        猜你喜欢
        • 2017-11-23
        • 2019-02-18
        • 2016-09-02
        • 2017-07-09
        • 1970-01-01
        • 2023-02-03
        • 2022-01-18
        • 2016-02-15
        • 1970-01-01
        相关资源
        最近更新 更多