typescript声明文件写法

2020-04-15  本文已影响0人  一懒众衫小小小小

引入模块文件

以这三个文件为例,分别以模块导出声明 (External Module Definition)全局类型声明(Global Type Definition) 两种写法编写 Definition。

入口文件引用模块:

// file: index.js
var sample = require("sample");
var lib1 = require("sample/lib1");
var lib2 = require("sample/lib2");

假设以下为三个文件代码,后面以这三个文件基础编写Definition:

// file: ./node_modules/sample/index.js
var abc = 321;
exports.setABC = function(abcValue) {
abc = abcValue;
};
exports.getABC = function() {
return abc;
};
 
exports.defaultABC = abc;
// file: ./node_modules/sample/lib1.js
var Hello = (
    function () {
        function Hello(a) {
        this.valueA = a;
    }

    Object.defineProperty(
    Hello.prototype,"a", {
        get: function () {
            return this.valueA;
        },
        enumerable: true,
        configurable: true
    });
    Hello.initClass = function () {
        Hello.initCount = 0;
    };

    /* 假设这是一个重载函数,支持多种调用方式 */
    
    Hello.prototype.setup = function (x, b) {
        if (b === void 0) { 
            b = null; 
        }
        return false;
    };
    return Hello;
}());

exports.Hello = Hello;
// file: ./node_modules/sample/lib2.js
 
var randStrSeed = "abcdefghijklmnopuvwxyz012345";
 
function randomString(length) {
    var ret = "";
 
    while (length-- > 0) {
        ret += randStrSeed[Math.floor(Math.random() * randStrSeed.length)];
    }
 
    return ret;
}
 
module.exports = randomString;

这是三个典型的模块类型:

全局类型声明写法

假如上面3个文件同属一个模块sample,但它并不是我们自己发布到npm的包,即我们无权为它建Definition,所以我们用全局声明写法。

如果不是很复杂,我们用一个.d.ts文件就可以了。

第一个文件是模块入口文件,导出了变量和函数,可以直接当作模块sample:

declare module "sample" {
 
    // 导出函数 setABC
    export function setABC(abcValue: number): void;
 
    // 导出函数 getABC
    export function getABC(): number;
 
    // 导出变量 defaultABC
    export let defaultABC: number;
}

第二个文件导出了两个类,可以当作模块"sample/lib1"。
这个类里面有构造函数,有静态方法,有普通方法,有属性,也有静态属性,还有 getter。

类有两种声明编写方式:标准式和分离式。
保准方式:

declare module "sample/lib1" {
    export class Hello {
        private valueA;
        b: number;
        static initCount: number;
        a: number;
        constructor(a: number);
        static initClass(): void;
        // 假设这是重载函数,支持多种调用方式
        setup(name: string): boolean;
        setup(name: string, age: number): boolean;
    }
}

但是这种写法也有不便的地方,比如扩展类不方便——JavaScript允许你随时扩展一个类的原型对象实现对类的扩展,或者随时给类添加静态成员。标准式写法很难实现扩展,因为你无法重复声明一个类。

分离式声明:
在这之前我们要理解,JS 的类是用函数实现的,即是说 JS 的类本质上就是一个构造函数 + Prototype。Prototype 的成员就是类的成员;而类的静态方法就是这个构造函数对象本身的成员方法。

declare module "sample/lib1" {

    // 在分离式写法里面,一个类的 Prototype 的声明是一个直接以类名称为名的interface。
    // 成员函数和变量/getter/setter 都行写在 prototype的接口里面。
    
    // 注:类原型的interface取名与类一致
    export interface Hello {
    
        // 接口里面只写类的 public 属性
        b: number;
        
        // Getter/Setter 直接成属性即可
        a: number;
        
        // 重载函数的声明写法
        setup(name: string): boolean;
        setup(name: string, age: number): boolean;
    }
    
    // 在分离式写法里面,一个类的构造函数对象也是一个 interface ,但是对
    // 其命名无具体要求,合理即可。
    
    // 把类的静态方法和属性都写在这里面。
    export interface HelloConstructor {
    
        // 静态属性
        initCount: number;
        
        // 静态方法
        initClass: void;
        
        // 构造函数!使用 new 代替 constructor,并声明其返回值类型是该类的Prototype。
        new(a: number): Hello;
    }
    
     // 将 Hello 覆盖声明为HelloConstructor。
        // 这样,在需要作为类使用的时候它就是 HelloConstructor,需要作为接口使用的时候就是 Hello(原型接口)。
    
    export let Hello: HelloConstructot;
}

第三个文件,直接将一个函数作为模块导出。

declare module "sample/lib2" {
    let randomString: (length: number) => string;
    
    export = randomString;
}

最后把 3 个模块的声明合并成一个文件 sample.d.ts,在文件里用三斜线指令引用即可。

模块导出声明写法

模块导出声明写法里面不用注明是哪个模块,一般给每个导出的文件都配备一个以 .d.ts 为后缀的 Definition。

文件 ./node_modules/sample/index.d.ts

// File: ./node_modules/sample/index.d.ts

// 导出函数 setABC
export declare function setABC(abcValue: number): void;

// 导出函数 getABC
export declare function getABC(): number;
 
// 导出变量 defaultABC
export declare let defaultABC: number;

文件 ./node_modules/sample/lib1.d.ts

// File: ./node_modules/sample/lib1.d.ts
 
export class Hello {
 
    private valueA;
 
    b: number;
 
    static instCount: number;
 
    a: number;
 
    constructor(a: number);
 
    static initClass(): void;
 
    //假设这是一个重载函数,支持多种调用方式
    setup(name: string): boolean;
 
    setup(name: string, age: number): boolean;
}

文件 ./node_modules/sample/lib2.d.ts

// File: ./node_modules/sample/lib2.d.ts
 
let randomString: (length: number) => string;
 
export = randomString;

编写Definition注意事项

  1. 不要使用内层declare

只能在 Definition 的顶层使用 declare,比如下面的写法都是错误的:

declare module "sample" {
    // 此处应当使用 export
    declare let a: string;
}
 
declare namespace Sample {
    // 此处应当使用 export
    declare let a: string;
}
  1. 避免全局污染

虽然全局声明写法允许你引入名称到全局命名空间中,但这也意味着,引入的顶层名称都是全局的。所以应该将所有的模块内导出的元素都放进模块或者命名空间内:

declare module "sample" {
   //仅可通过 import { Person } from "sample" 访问。
   export interface Person {
       name: string;
   }
}

declare namespace Sample {
   export interface Animal {
       type: string;
   }
}

而不是

// 无需 import 即可使用,即全局的
interface Person {
   name: string;
}
  1. 注意声明冲突
  2. 模块名称要区分大小写!

参考文档:https://www.cnblogs.com/dhcn/p/7722248.htm

上一篇下一篇

猜你喜欢

热点阅读