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

225

我使用了很多自己的和第三方的库。 我看到"typings"目录中包含一些Jquery和WinRT的库...但它们是如何创建的呢?

10个回答

270

根据所使用的库、它的编写方式以及您需要的准确性水平,有几种选项可供选择。让我们大致按照可取性降序来审查这些选项。

也许已经存在

首先,始终要先检查 DefinitelyTyped (https://github.com/DefinitelyTyped/DefinitelyTyped)。这是一个社区存储库,包含成千上万个 .d.ts 文件,很可能您正在使用的东西已经在里面了。 您还应该检查 TypeSearch (https://microsoft.github.io/TypeSearch/),这是一个搜索引擎,用于搜索 NPM 发布的 .d.ts 文件;这将比 DefinitelyTyped 有更多的定义。 一些模块也会将其自己的定义作为其 NPM 分发的一部分进行发布,因此,请在尝试编写自己的定义之前看看是否已经存在。

也许您不需要定义文件

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

使用 --allowJs 入手

如果 --allowJs 给出了合理的结果,并且您想编写更好的定义文件,则可以将 --allowJs--declaration 结合起来,查看 TypeScript 对库类型的“最佳猜测”。这将为您提供一个不错的起点,如果 JSDoc 注释编写得很好并且编译器能够找到它们,可能与手工编写的文件一样好。

使用 dts-gen 入手

如果 --allowJs 不起作用,则可以使用 dts-gen (https://github.com/Microsoft/dts-gen) 来获得一个起点。此工具使用对象的运行时形状来准确枚举所有可用属性。好处是这通常非常准确,但该工具尚不支持抓取 JSDoc 注释以填充其他类型。您可以像这样运行它:

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

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

打个盹

如果你只想稍后完成所有工作并暂时不需要类型,在TypeScript 2.0中,现在可以编写以下内容:

declare module "foo";

这将让你使用类型any导入"foo"模块。如果你想以后处理全局变量,只需编写:

declare const foo: any;

这将给你一个foo变量。


26
哎呀...这将成为许多情况下的重要门槛。有一个工具能够基于类型推断输出至少“最佳猜测”将是很好的。虽然这些可能不是最优的,但它们可以随着时间的推移进行调整。 - Hotrodmonkey
7
更好的消息是:--declarations选项可以同时生成.js.d.ts两个文件,这意味着你只需要运行一次编译即可。 - Fenton
67
在 https://github.com/borisyankov/DefinitelyTyped 可以找到一系列流行库的高质量定义。 - Boris Yankov
10
“最佳猜测”工具并不比TypeScript中已经存在的IntelliSense更好。类型定义的好处在于为编译器提供比它可以“猜测”的更多信息。 - Boris Yankov
13
在 TypeScript 1.8 和 2.0 中,--allowJs--declaration 选项不能组合使用。如果尝试这样做,会出现以下错误: error TS5053: Option 'allowJs' cannot be specified with option 'declaration' - Aleksei Zyrianov
显示剩余5条评论

70

你可以按照Ryan所述使用tsc --declaration fileName.ts,或者在你的tsconfig.json文件中,在compilerOptions下指定declaration: true,前提是你已经有了一个tsconfig.json文件。


4
如果您正在开发一个Angular 2模块并希望与他人共享,那么这是最佳答案。 - Darcy
如果我尝试运行 tsc --declaration test.ts,我会得到错误信息 Cannot find name...,这是因为我正在尝试创建一个声明文件 :) 所以在声明之前我需要先有这些类型? - Kokodoko
2
@Kokodoko,你可以尝试在你的tsconfig.json文件中添加declaration: true吗? - JayKan
谢谢,我只是想从*.ts生成*.d.ts。 - vintprox

20

创建自己的库时,您可以使用 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文件)

(有关所有命令行编译器选项,请参见文档

或者例如在您的package.json中:

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

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


1
我仍然无法弄清楚如何生成 d.ts 文件并能够使用接口。你有任何例子吗? - Danosaure
上面的npm脚本将生成*.d.ts文件并将它们放在dist/lib文件夹中。你确实需要在项目根目录下有一个tsconfig.json文件,但这对于项目的正常工作来说应该已经存在了。 - magikMaker
一旦暴露出来,我就无法使用它们。如果您能让它工作,我有以下帖子 https://stackoverflow.com/questions/59742688。 - Danosaure

17

如果在DefinitelyTyped上找不到声明文件,最好的解决方法是只为你使用的部分编写声明文件,而不是整个库。这样可以大大减少工作量,此外编译器会通过报告缺失的方法来提供帮助。


2
文档很遗憾地是无望的。 - Alper

17

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


1
似乎目前将 *.js 文件传递给 tsc 会生成错误 "无法读取文件“angular-resource.js”:文件未找到"。因此,您必须明确将 *.js 文件重命名为 *.ts 才能使用 tsc。有关更多详细信息,请参见此问题 - 我尝试在 angularjs 上使用它:http://typescript.codeplex.com/workitem/26 - James Strachan
3
据我所知,tsc可以编译任何有效的JavaScript代码,但如果它不是有效的TypeScript代码(编译时会发出警告),那么--declarations标志就不会输出.d.ts文件。因此,除非您的库是用TypeScript编写的,否则仍需要手动创建声明文件。 - agradl

7

3

以下是一些PowerShell代码,用于创建一个包含多个现代JavaScript文件*.js的库的单个TypeScript定义文件。

首先,将所有扩展名更改为.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文件,其中包含许多定义。


2
第二步,Get-ChildItem | foreach { tsc --declaration $_.Name } 已经生效(添加了缺失的 --declaration 参数)。 - Guru_07

2
这个答案展示了如何使用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;
};

该函数来源于TypeScript编译器API指南中的文档

2
我会寻找现有的与Script#或SharpKit兼容的第三方JS库映射。使用这些C#转.js跨编译器的用户将面临您现在面临的问题,他们可能已经发布了一个开源程序来扫描您的第三方库并将其转换为基本的C#类。如果是这样,请修改扫描程序以生成TypeScript而不是C#。
如果找不到现成的映射,将你的第三方库的C#公共接口转化为TypeScript定义可能比读取JavaScript源代码更简单。
我特别关注Sencha's ExtJS RIA框架,我知道已经有项目发布可以为Script#或SharpKit生成C#解释。

FYI:我刚刚为Sencha/ExtJS创建了我的第一个TypeScript定义文件:https://github.com/andremussche/extjsTypescript/blob/master/jsondocs/ext-4.1.1a.d.ts - André
嘿,Camel Case,我写了一篇关于如何使用 TypeScript 和 ExtJs 的博客:http://blorkfish.wordpress.com/2013/01/28/using-extjs-with-typescript/ - blorkfish

2
为了让像我这样的 TypeScript 新手更容易理解,我们需要把事情变得非常简单:
tsc --declaration FileName.js --allowJs

这个命令将在您当前文件夹中生成一个 d.ts 文件。您需要安装 TypeScript,但不需要成为项目维护者,也不需要设置更大的编译过程。您可以将任何旧的 JavaScript 文件交给它,并尝试为您生成类型定义。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接