当使用第三方库时,需要引用它的声明文件,才能获知其数据类型,以获得对应的代码补全、接口提示等功能。
声明文件用于定义类型而非具体的值,不会保留在编译结果的 js 中。
TS编译检查时,变量的声明查找顺序
- 当前编译上下文找该变量的定义
- 变量所在模块的
index.d.ts声明文件中查找(或其package.json中types字段指向文件)
通常由npm包的维护者提供 -
node_modules/@types/npm包名查找声明
通常是作者没有提供声明文件,由社区其他开发者提供,以@types/开头的声明文件,如:npm install @types/jquery - 通过配置文件
tsconfig.json中的paths和baseUrl字段指向的其他目录
通常是自己本地写的声明文件
生成 .d.ts 文件
在tsc指令中添加 --declaration(简写 -d),或者在 tsconfig.json 中添加 declaration 选项,就可以同时也生成 .d.ts 声明文件了。
{
"compilerOptions": {
"module": "commonjs",
"outDir": "lib",
"declaration": true,
}
}
全局变量声明
在script模式中,通过declare进行全局变量声明,通过script标签引入,全局生效。
注意全局变量文件中不能有import、export关键字,否则会被视为npm/UMD包文件。
type 和 interface
两者本来就只是类型的定义,因此不需搭配declare关键字,直接在声明文件中使用
为了防止命名冲突,通常放在namespace下
声明全局变量
declare var jQuery: (selector: string) => any;
declare let jQuery: (selector: string) => any;
declare const jQuery: (selector: string) => any;
声明函数
支持函数重载
declare function jQuery(selector: string): any;
declare function jQuery(domReadyCallback: () => any): any;
声明类
注意 declare 里可以声明类的构造函数,interface 里不可以
declare class Animal {
name: string;
constructor(name: string);
sayHi(): string;
}
声明枚举
declare enum Directions {
Up,
Down,
Left,
Right
}
声明命名空间
命名空间(namespace)是TS自己的模块化方案,随着ES6的module广泛运用,已不推荐使用。不同于ES6的module,一个文件里可以有多个namespace。
目前通常在声明文件中使用,表示一个对象中具有很多子属性。
通过命名空间.属性来调用内部属性。
declare namespace jQuery {
function ajax(url: string, settings?: any): void;
const version: number;
class Event {
blur(eventType: EventType): void
}
enum EventType {
CustomClick
}
}
命名空间也可以嵌套
// src/jQuery.d.ts
declare namespace jQuery {
function ajax(url: string, settings?: any): void;
namespace fn {
function extend(object: any): void;
}
}
//或
declare namespace jQuery.fn {
function extend(object: any): void;
}
// src/index.ts
jQuery.ajax('/api/get_something');
jQuery.fn.extend({
check: function() {
return this.each(function() {
this.checked = true;
});
}
});
声明合并
同名变量可以重复声明,表示有不同的形状
declare function jQuery(selector: string): any;
declare namespace jQuery {
function ajax(url: string, settings?: any): void;
}
通过声明合并,还可以为系统变量添加新属性,称为扩展全局变量:
interface String {
prependHello(): string;
}
模块声明
npm包(ES6)规范的声明
.d.ts文件中,如存在export,则为一个模块声明文件,npm包中即使用该方式。
在模块声明中,只有export导出 + import导入的声明,才会生效。
可以对每个变量分别export,也可以用declare分别声明,并统一export(注意,同全局变量声明文件,type 和 interface 不需要 declare)
// types/foo/index.d.ts
export const name: string;
export interface Options {
data: any;
}
// types/foo/index.d.ts
declare const name: string;
interface Options {
data: any;
}
export { name, Options };
// src/index.ts
import { name, Options } from 'foo';
console.log(name);
let options: Options = {
data: {
name: 'foo'
}
};
commonjs 规范的声明
ts中,对采用commonjs规范的库,可以通过以下方式导入:
- commonjs语法:
const foo = require('foo');
const bar = require('foo').bar;
- ESM语法:
import * as foo from 'foo';
import { bar } from 'foo';
- TS新增语法
import + require和export =搭配使用:
// types/foo/index.d.ts
export = foo;
declare function foo(): string;
declare namespace foo {
const bar: number;
}
// 整体导入
import foo = require('foo');
import * as foo from 'foo';
import foo from 'foo';//当启用allowSyntheticDefaultImports时
// 单个导入
import bar = foo.bar;
UMD规范的声明
既可以通过 <script> 标签引入,又可以通过 import 导入的库,需要通过export as namespace将声明好的一个变量声明为全局变量
// types/foo/index.d.ts
export as namespace foo;
export = foo;
declare function foo(): string;
declare namespace foo {
const bar: number;
}
直接扩展全局变量
有的第三方库扩展了一个全局变量,可是此全局变量的类型却没有相应的更新过来,就会导致 ts 编译错误,此时就需要扩展全局变量的类型。
注意声明文件仍然需要导出一个空对象,用来告诉编译器这是一个模块的声明文件
// types/foo/index.d.ts
declare global {
//通过声明合并,给 String 添加属性或方法。
interface String {
prependHello(): string;
}
}
export {};
// src/index.ts
'bar'.prependHello();
模块插件
declare module可用于在一个文件中一次性声明多个模块的类型,或扩展某模块类型
// types/moment-plugin/index.d.ts
import * as moment from 'moment';
declare module 'moment' {
export function foo(): moment.CalendarKey;
}
// src/index.ts
import * as moment from 'moment';
import 'moment-plugin';
moment.foo();
三斜线指令
类似于声明文件中的 import,它可以用来导入另一个声明文件。
三斜线指令必须放在文件的最顶端,三斜线指令的前面只允许出现单行或多行注释。
当且仅当在以下几个场景下,我们才需要使用三斜线指令替代 import:
- 当我们在书写一个全局变量的声明文件时(避免出现
import、export关键字,以免被识别为npm、UMD文件)
// types/jquery-plugin/index.d.ts
/// <reference types="jquery" />
declare function foo(options: JQuery.AjaxSettings): string;
// src/index.ts
foo({});
- 当我们需要依赖一个全局变量的声明文件时
由于全局变量不支持通过 import 导入,当然也就必须使用三斜线指令来引入了
// types/node-plugin/index.d.ts
/// <reference types="node" />
export function foo(p: NodeJS.Process): string;
// src/index.ts
import { foo } from 'node-plugin';
foo(global.process);