声明文件

  • 从类型type角度分为:基本类型(string、number、boolean等)及其混合;复杂类型(class、function、object)及其混合(比如说又是class又是function)。

  • 从代码有效范围分为:全局变量、模块变量和又是全局变量又是模块变量的。

  • 从定义文件来说:自己写的.d.ts文件和扩展别人写的.d.ts文件。

支持语法

declare var             // 声明全局变量
declare function        // 声明全局方法
declare class           // 声明全局类
declare enum            // 声明全局枚举类型
declare namespace       // 声明(含有子属性的)全局对象
interface || type       // 声明全局类型
export                  // 导出变量
export namespace        // 导出(含有子属性的)对象
export default          // ES6 默认导出
export =                // commonjs 导出模块
export as namespace     // UMD 库声明全局变量
declare global          // 扩展全局变量
declare module          // 扩展模块
/// <reference />       // 三斜线指令

声明文件应用场景

主要是为了解决在ts中第三方组件库或者插件中,第三方库引用的问题。

例如,代码中用到了jQuery,需要到 .d.ts 文件中声明,不然TS语法会报找不到全局变量 jquery$对象;

// shime-global.d.ts

declare var jQuery: (selector: string) => any;
declare var $: (selector: string) => any;

然后在改目录下所有ts中使用 jQuery, $ 对象是,ts便不会报错。

jQuery('#foo');

上例中,declare var 并没有真的定义一个变量,只是定义了全局变量 jQuery 的类型,仅仅会用于编译时的检查,在编译结果中会被删除。它编译结果是:

jQuery('#foo');

补充:上面声明文件为什么是.d.ts,这是ts模块解析规定的,先从.ts文件中查找,然后再查找 .d.ts文件中查找。

先让我们看看在工程中一般有哪些声明:

我们经常这样配置全局变量,如果第三方库没有声明,当前代码中没有定义的,都需要声明。

vue 模块的声明

// shims-vue.d.ts

declare module '*.vue' {
  import Vue from 'vue';
  export default Vue;
}

全局变量 模块

// shmie-global.d.ts

declare var window: Window;
declare var document: Document;

declare module "element-ui";

// ...

那么有些同学就要问了,我们用的很多npm,npm install之后,好像也没有声明,在ts中使用编译也没有报错啊。那是因为npm中已经声明了。

书写声明文件

当一个第三方库没有提供声明文件时,我们就需要自己书写声明文件了。

第三方库有以下几种使用场景

  • 全局变量:通过 标签引入第三方库,注入全局变量

  • npm 包:通过 import foo from 'foo' 导入,符合 ES6 模块规范

  • UMD 库:既可以通过 标签引入,又可以通过 import 导入

  • 直接扩展全局变量:通过 标签引入后,改变一个全局变量的结构

  • 在 npm 包或 UMD 库中扩展全局变量:引用 npm 包或 UMD 库后,改变一个全局变量的结构

  • 模块插件:通过 或 import 导入后,改变另一个模块的结构

全局变量的声明

全局变量的声明主要用以下几种

  • declare var 声明全局变量

  • declare function 声明全局方法

  • declare class 声明全局类

  • declare enum 声明全局枚举类型

  • declare namespace 声明(含有子属性的)全局对象

  • interfacetype 声明全局类型

举个例子:声明一个class 对象 Animal

// Animal.d.ts 

declare class Animal {
    name: string;
    constructor(name: string);
    sayHi(): string;
}
// src/index.ts

let cat = new Animal('Tom');

npm包的声明

一般来说,npm 包的声明文件可能存在于两个地方:

  1. 与该 npm 包绑定在一起。判断依据是 package.json 中有 types 字段,或者有一个 index.d.ts 声明文件。这种模式不需要额外安装其他包,是最为推荐的,所以以后我们自己创建 npm 包的时候,最好也将声明文件与 npm 包绑定在一起。

  1. 发布到 @types 里。我们只需要尝试安装一下对应的 @types 包就知道是否存在该声明文件,安装命令是 npm install @types/foo --save-dev。这种模式一般是由于 npm 包的维护者没有提供声明文件,所以只能由其他人将声明文件发布到 @types 里了。

假如以上两种方式都没有找到对应的声明文件,那么我们就需要自己为它写声明文件了.

有两种方案,一种自己写@types/xxx,发布上去。另一个直接在工程目录下声明,建议新建一个目录types,声明文件放在types

npm中声明和全局变量声明的区别

npm 包的声明文件与全局变量的声明文件有很大区别。在 npm 包的声明文件中,使用 declare 不再会声明一个全局变量,而只会在当前文件中声明一个局部变量。只有在声明文件中使用 export 导出,然后在使用方 import 导入后,才会应用到这些类型声明。

// types/foo/index.d.ts

export const name: string;
export function getName(): string;
export class Animal {
    constructor(name: string);
    sayHi(): string;
}
export enum Directions {
    Up,
    Down,
    Left,
    Right
}
export interface Options {
    data: any;
}
// src/index.ts

import { name, getName, Animal, Directions, Options } from 'foo';

当然也可以混用

declare const name: string;
declare function getName(): string;
declare class Animal {
    constructor(name: string);
    sayHi(): string;
}
declare enum Directions {
    Up,
    Down,
    Left,
    Right
}
interface Options {
    data: any;
}

export { name, getName, Animal, Directions, Options };

上面 Axios就是这样做的。

UMD库中的声明

在 npm 包或 UMD 库中扩展全局变量

对于 npm 包或 UMD 库,如果导入此库之后会扩展全局变量,则需要使用另一种语法在声明文件中扩展全局变量的类型,那就是 declare global

// types/foo/index.d.ts

declare global {
    interface String {
        prependHello(): string;
    }
}

export {};


// src/index.ts
'bar'.prependHello();

声明模块插件

发布声明文件

两种方式

  • 将声明文件和源码放在一起

  • 发布到@types

将声明文件和源码放在一起

  • 自动生成,tsc自动生成

  • 手动声明

如果是手动写的声明文件,那么需要满足以下条件之一,才能被正确的识别:

  • package.json 中的 typestypings 字段指定一个类型声明文件地址

  • 在项目根目录下,编写一个 index.d.ts 文件

  • 针对入口文件(package.json 中的 main 字段指定的入口文件),编写一个同名不同后缀的 .d.ts 文件

{
    "name": "foo",
    "version": "1.0.0",
    "main": "lib/index.js",
    "types": "foo.d.ts",
}

如果没有指定typestypings,那么就会在根目录下寻找 index.d.ts 文件,将它视为此库的类型声明文件。

如果没有找到 index.d.ts 文件,那么就会寻找入口文件(package.json 中的 main 字段指定的入口文件)是否存在对应同名不同后缀的 .d.ts 文件。

如果还是不存在,那么就会寻找是否存在 lib/index.d.ts 文件。假如说连 lib/index.d.ts 都不存在的话,就会被认为是一个没有提供类型声明文件的库了。

参考

typescript-tutorial

Last updated