TypeScript 声明文件

JavaScript中一个库有很多使用方式,这就需要你书写声明文件去匹配它们。 这篇涵盖了如何识别常见库的模式,和怎样书写符合相应模式的声明文件。

全局库

全局库是指能在全局命名空间下访问的(例如:不需要使用任何形式的import)。 许多库都是简单的暴露出一个或多个全局变量。 比如,如果你使用过 jQuery$变量可以被够简单的引用:
全局库的指南文档上看到如何在HTML里用脚本标签引用库:

<script src="http://a.great.cdn.for/someLib.js"></script>

目前,大多数流行的全局访问型库实际上都以UMD库的形式进行书写(见后文)。 UMD库的文档很难与全局库文档两者之间难以区分。 在书写全局声明文件前,一定要确认一下库是否真的不是UMD。

从代码上识别全局库
全局库的代码通常都十分简单。 一个全局的“Hello, world”库可能是这样的:

function createGreeting(s) {
    return "Hello, " + s;
}
// or
window.createGreeting = function(s) {
    return "Hello, " + s;
}

当你查看全局库的源代码时,你通常会看到:

  • 顶级的var语句或function声明
  • 一个或多个赋值语句到window.someName
  • 假设DOM原始值像documentwindow是存在的

你不会看到:

  • 检查是否使用或如何使用模块加载器,比如requiredefine
  • CommonJS/Node.js风格的导入如var fs = require("fs");
  • define(...)调用
  • 文档里说明了如何去require或导入这个库

例子:

// Type definitions for [~THE LIBRARY NAME~] [~OPTIONAL VERSION NUMBER~]
// Project: [~THE PROJECT NAME~]
// Definitions by: [~YOUR NAME~] <[~A URL FOR YOU~]>

/*~ If this library is callable (e.g. can be invoked as myLib(3)),
 *~ include those call signatures here.
 *~ Otherwise, delete this section.
 */
declare function myLib(a: string): string;
declare function myLib(a: number): number;

/*~ If you want the name of this library to be a valid type name,
 *~ you can do so here.
 *~
 *~ For example, this allows us to write 'var x: myLib';
 *~ Be sure this actually makes sense! If it doesn't, just
 *~ delete this declaration and add types inside the namespace below.
 */
interface myLib {
    name: string;
    length: number;
    extras?: string[];
}

/*~ If your library has properties exposed on a global variable,
 *~ place them here.
 *~ You should also place types (interfaces and type alias) here.
 */
declare namespace myLib {
    //~ We can write 'myLib.timeout = 50;'
    let timeout: number;

    //~ We can access 'myLib.version', but not change it
    const version: string;

    //~ There's some class we can create via 'let c = new myLib.Cat(42)'
    //~ Or reference e.g. 'function f(c: myLib.Cat) { ... }
    class Cat {
        constructor(n: number);

        //~ We can read 'c.age' from a 'Cat' instance
        readonly age: number;

        //~ We can invoke 'c.purr()' from a 'Cat' instance
        purr(): void;
    }

    //~ We can declare a variable as
    //~   'var s: myLib.CatSettings = { weight: 5, name: "Maru" };'
    interface CatSettings {
        weight: number;
        name: string;
        tailLength?: number;
    }

    //~ We can write 'const v: myLib.VetID = 42;'
    //~  or 'const v: myLib.VetID = "bob";'
    type VetID = string | number;

    //~ We can invoke 'myLib.checkCat(c)' or 'myLib.checkCat(c, v);'
    function checkCat(c: Cat, s?: VetID);
}

模块化库

一些库只能工作在模块加载器的环境下。 比如,像express只能在Node.js里工作所以必须使用CommonJSrequire函数加载。

ECMAScript 2015(也就是ES2015ECMAScript 6ES6),CommonJSRequireJS具有相似的导入一个模块的表示方法。 例如,

// 对于`JavaScript CommonJS (Node.js)`,有下面的代码
var fs = require("fs");
// 对于TypeScript或ES6,import关键字也具有相同的作用:
import fs = require("fs");
// 你通常会在模块化库的文档里看到如下说明:
var someLib = require('someLib');
// or 
define(..., ['someLib'], function(someLib) {
});

TypeScript 声明文件 -- 结构

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容