简介
声明文件是以.d.ts为后缀的文件,开发者在声明文件中编写类型声明,TypeScript根据声明文件的内容进行类型检查。(注意同目录下最好不要有同名的.ts文件和.d.ts,例如lib.ts
和lib.d.ts
,否则模块系统无法只根据文件名加载模块)
为什么需要声明文件呢?我们知道TypeScript根据类型声明进行类型检查,但有些情况可能没有类型声明:
- 第三方包,因为第三方包打包后都是JavaScript语法,而非TypeScript,没有类型。
- 宿主环境扩展,如一些hybrid环境,在window变量下有一些bridge接口,这些接口没有类型声明。
如果没有类型声明,在使用变量、调用函数、实例化类的时候就没法通过TypeScript的类型检查。
声明文件就是针对这些情况,开发者在声明文件中编写第三方模块的类型声明/宿主环境的类型声明。让TypeScript可以正常地进行类型检查。
除此之外,声明文件也可以被导入,使用其中暴露的类型定义。
总之,声明文件有两种用法:
- 被通过import导入,使用其中暴露的类型定义和变量声明。
- 和相关模块关联,为模块进行类型声明。
对于第二种用法,声明文件如何同相关模块关联呢?
比如有个第三方包名字叫"foo",那么TypeScript会在node_modules/foo
中根据其package.json的types和typing字段查找声明文件查找到的声明文件被作为该模块的声明文件;TypeScript也会在node_modules/@types/foo/
目录中查找声明文件,如果能找到就被作为foo模块的声明文件;TypeScript还会在我们的项目中查找.d.ts文件,如果遇到declare module 'foo'
语句,则该声明被用作foo模块的声明。
总结一下,TypeScript会在特定的目录读取指定的声明文件。
- 在内部项目中,TypeScript会读取tsconfig.json中的文件集合,在其中的声明文件才会被处理。
- 读取node_modules中各第三方包的package.json的
types
或者typing
指定的文件。 - 读取@types目录下同名包的声明文件。
声明文件中的代码不会出现在最终的编译结果中,编译后会把转换后的JavaScript代码输出到"outDir"选项指定的目录中,并且把 .ts模块中使用到的值的声明都输出到"declarationDir"指定的目录中。
而在.ts文件中的声明语句,编译后会被去掉,如
declare let a: number;
export default a;
会被编译为
"use strict";
exports.__esModule = true;
exports["default"] = a;
TypeScript编译过程不仅将TypeScript语法转译为ES6/ES5,还会将代码中.ts文件中用到的值的类型输出到指定的声明文件中。如果你需要实现一个库项目,这个功能很有用,因为用到你的库的项目可以直接使用这些声明文件,而不需要你再为你的库写声明文件。
语法
内容
TypeScript中的声明会创建以下三种实体之一:命名空间,类型或值。
命名空间最终被编译为全局变量,因此我们也可以认为声明文件中其实创建了类型和值两种实体。即定义类型或者声明值。
// 类型 接口
interface Person {name: string;}
// 类型 类型别名
type Fruit = {size: number};
// 值 变量
declare let a: number;
// 值 函数
declare function log(message: string): void;
// 值 类
declare class Person {name: string;}
// 值 枚举
declare enum Color {Red, Green}
// 值 命名空间
declare namespace person {let name: string;}
我们注意到类型可以直接定义,但是值的声明需要借助declare关键字,这是因为如果不用declare关键字,值的声明和初始化是一起的,如
let a: number;
// 编译为
var a;
但是编译结