# 声明文件
当使用第三方库时,我们需要引用它的声明文件,才能获得对应的代码补全、接口提示等功能。
当一个第三方库只是一个 JavaScript 库的时候,为了提升在 TypeScript 的开发体验,我们就需要对这个第三方库进行类型声明。
# 声明语法索引
全局
declare var 声明全局变量 declare function 声明全局方法 declare class 声明全局类
拓展
declare global 扩展全局变量 declare module 扩展模块
# 什么是声明语句
假如我们想使用第三方库 jQuery,一种常见的方式是在 html 中通过 <script>
标签引入 jQuery,然后就可以使用全局变量 $ 或 jQuery 了。
我们通常这样获取一个 id 是 foo 的元素:
$("#foo");
// or
jQuery("#foo");
但是在 ts 中,编译器并不知道 $ 或 jQuery 是什么东西 1:
jQuery("#foo");
// ERROR: Cannot find name 'jQuery'.
这时,我们需要使用 declare var 来定义它的类型 2:
declare var jQuery: (selector: string) => any;
jQuery("#foo");
# 什么是声明文件
通常我们会把声明语句放到一个单独的文件(jQuery.d.ts)中,这就是声明文件 3:
// src/jQuery.d.ts
declare var jQuery: (selector: string) => any;
声明文件必需以 .d.ts 为后缀。
一般来说,ts 会解析项目中所有的 *.ts 文件,当然也包含以 .d.ts 结尾的文件。
所以当我们将 jQuery.d.ts 放到项目中时,其他所有 *.ts 文件就都可以获得 jQuery 的类型定义了。
当然,jQuery 的声明文件不需要我们定义了,社区已经帮我们定义好了,我们可以直接下载下来使用。
npm install @types/jquery --save-dev
# 编写声明文件
当一个第三方库没有提供声明文件时,我们就需要自己书写声明文件了
TIP
这里暂时只关注对 npm 包的声明
一般我们通过 import foo from 'foo' 导入一个 npm 包,这是符合 ES6 模块规范的。
在我们尝试给一个 npm 包创建声明文件之前,需要先看看它的声明文件是否已经存在。一般来说,npm 包的声明文件可能存在于两个地方
- 与该 npm 包绑定在一起。判断依据是 package.json 中有 types 字段,或者有一个 index.d.ts 声明文件
- 发布到 @types 里。我们只需要尝试安装一下对应的 @types 包就知道是否存在该声明文件
假如以上两种方式都没有找到对应的声明文件,那么我们就需要自己为它写声明文件了
一般建议创建一个 types 目录,专门用来管理自己写的声明文件
比如将 foo 的声明文件放到 types/foo/index.d.ts 中。这种方式需要配置下 tsconfig.json 中的 paths 和 baseUrl 字段。
{
"compilerOptions": {
"module": "commonjs",
"baseUrl": "./",
"paths": {
"*": ["types/*"]
}
}
}
export
export 的语法与普通的 ts 中的语法类似,区别仅在于声明文件中禁止定义具体的实现
// 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;
}
混用 declare 和 export
我们也可以使用 declare 先声明多个变量,最后再用 export 一次性导出
// types/foo/index.d.ts
declare const name: string;
declare function getName(): string;
declare class Animal {
constructor(name: string);
sayHi(): string;
}
declare enum Directions {
Up,
Down,
Left,
Right
}
// interface 前是不需要 declare 的
interface Options {
data: any;
}
export { name, getName, Animal, Directions, Options };
export namespace
export namespace
用来导出一个拥有子属性的对象
// types/foo/index.d.ts
export namespace foo {
const name: string;
namespace bar {
function baz(): string;
}
}
// src/index.ts
import { foo } from "foo";
console.log(foo.name);
foo.bar.baz();
export default
在 ES6 模块系统中,使用 export default 可以导出一个默认值
// types/foo/index.d.ts
export default function foo(): string;
// src/index.ts
import foo from "foo";
foo();
注意,只有 function、class 和 interface 可以直接默认导出,其他的变量需要先定义出来,再默认导出