如何从现有的 JavaScript 库生成 .d.ts “类型”定义文件?

Posted

技术标签:

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

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

【问题讨论】:

【参考方案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 文件,但无论如何项目都应该在那里工作。 一旦暴露,我将无法使用它们。我有以下帖子***.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# 解释

【讨论】:

仅供参考:我刚刚为 Sencha/ExtJS 制作了我的第一个打字稿定义文件:github.com/andremussche/extjsTypescript/blob/master/jsondocs/… 嘿 Camel Case,我整理了一篇关于使用 ExtJs 和 typescript 的博客:blorkfish.wordpress.com/2013/01/28/using-extjs-with-typescript【参考方案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

【讨论】:

以上是关于如何从现有的 JavaScript 库生成 .d.ts “类型”定义文件?的主要内容,如果未能解决你的问题,请参考以下文章

如何从现有的 WSDL 和 XSD 文件生成 WCF 服务主机

如何从现有的 MySQL 数据库生成创建用户脚本?

从现有的 python 包创建机器人框架库

如何从现有的远程分支创建本地分支?

从现有的 JAVA Spring API 生成 YAML 文件

CefSharp 从现有的 Javascript 函数中获取结果