为第三方JavaScript模块编写声明文件的方法

310 阅读3分钟

在TypeScript中,由于模块声明文件保存在.d.ts文件中,而且文件中不能有值,因此声明模块类型时要使用declare关键字声明模块中导出的指定类型的值。

TypeScript声明及相应的类型声明

.ts.d.ts
var a = 1declare var a: number
let a = 1declare let a: number
const a = 1declare const a: 1
function a(b) { return b.toFiexed() }declare function a(b: number): string
class A { b() { return 3 }}declare class A { b(): number }
namespace A {}declare namespace A {}
type A = numbertype A = number
interface A { b?: string }interface A { b?: string }

导出的类型

导出的方式不同,例如全局导出、ES2015导出或CommonJS导出,编写声明文件的方式也不同。

全局导出

如果在一个模块中只为全局命名空间赋值,而没有导出任何代码,只需创建一个脚本模式文件,在变量、函数和类声明前面加上declare(其他声明:例如enum、type等,保持不变):

// 全局变量
declare let someGlobal: GlobalType

// 全局类
declare class GlobalClass {}

// 全局函数
declare function globalFunction(): string

// 全局枚举
enum GlobalEnum (A, B, C)

// 全局命名空间
namespace GlobalNamespace {}

// 全局类型声明
type GlobalType = number;

// 全局接口
interface GlobalInterface {}

这些声明在项目中的任何文件里都可以使用,无须显示导入。比如说,在项目中的任何文件都可以使用someGlobal,无须事先导入,不过在运行时,someGlobal要赋值给全局命名空间(浏览器中的window, NodeJS中的global)。

注意:为了保证文件在脚本模式下,在声明文件中不能使用import和export。

ES2015导出

如果模块使用ES2015导出方式,即使用export关键字,把declare(申明定义了全局变量)替换为export(申明导出了ES2015绑定)即可:

// 默认导出
declare let defaultExport: SomeType
export default defaultExport

// 具名导出
export class SomeExport {
    a: SomeOtherType
}

// 类导出
export class ExportedClass {}

// 函数导出
export function exportedFunction(): string

// 枚举导出
enum ExportedEnum {A, B, C}

// 命名空间导出
export namespace SomeNamespace {
    let someNamespacedExport: number
}

// 类型导出
export type SomeType = {
    a: number
}

// 接口导出
export interface SomeOtherType {
    b: string
}

CommonJS导出

在ES2015之前,CommonJS是模块的标准,也是NodeJS的模块标准。CommonJS模块也使用export关键字导出代码,但是语法稍有不同:

declare let defaultExport: SomeType
export = defaultExport

注意:这里是把要导出的代码赋值给export,而不是把export用作修饰符。

第三方CommonJS模块的类型声明中只能有一个导出语句。如想导出多个模块,要利用声明合并行为。

例如,若想为多个导出声明类型,而且没有默认导出,可以导出一个命名空间:

declare namespace MyNamedExports {
    export let someExport: SomeType
    export type SomeType = number
    export class OtherExport {
        otherType: string
    }
}
export = MyNamedExports

如果CommonJS模块中既有默认导出也有具名导出,这时就要利用声明合并行为了:

declare namespace MyExports {
    export let someExport: SomeType
    export type SomeType = number
}
declare function MyExports(a: number): string
export = MyExports

UMD导出

为UMD模块提供类型信息的方式基本上与ES2015模块一样。唯一的区别是,如果想让模块在脚本模式下的文件中全局可用,要使用特殊的 export as namespace句法。例如:

// 默认导出
declare let defaultExport: SomeType
export default defaultExport

// 具名导出
export class SomeExport {
    a: SomeType
}

// 类型导出
export type SomeType = {
    a: number
}

export as namespace MyModule

注意最后一行。有了这一行,项目中处于脚本模式下的文件可以在全局命名空间MyModule上直接使用该模块(无须事先导入)

let a = new MyModule.SomeExport