TypeScript中d.ts类型声明文件的实现
作者:海阔天空BM
1. 简介
单独使用的模块,一般会同时提供一个单独的类型声明文件(declaration file),把本模块的外部接口的所有类型都写在这个文件里面,便于模块使用者了解接口,也便于编译器检查使用者的用法是否正确。
类型声明文件里面只有类型代码,没有具体的代码实现。它的文件名一般为[模块名].d.ts
的形式,其中的d
表示 declaration(声明)。
例如:某个模块
const maxInterval = 12; function getArrayLength(arr) { return arr.length; } module.exports = { getArrayLength, maxInterval, };
这个模块的类型声明文件
export function getArrayLength(arr: any[]): number; export const maxInterval: 12;
在类型声明文件中,模块输出可以写成export=
或者export default
表示
// 模块输出 module.exports = 3.142; // 类型输出文件 // 写法一 declare const pi: number; export default pi; // 写法二 declare const pi: number; export= pi;
类型声明文件也可以写在项目的 tsconfig.json 文件里面,这样的话,编译器打包项目时,会自动将类型声明文件加入编译,而不必在每个脚本里面加载类型声明文件。比如,moment 模块的类型声明文件是moment.d.ts
,使用 moment 模块的项目可以将其加入项目的 tsconfig.json 文件。
{ "compilerOptions": {}, "files": [ "src/index.ts", "typings/moment.d.ts" ] }
2. 类型声明文件的来源
有三种来源:1. ts编译器自动生成 2. ts内置类型文件 3. 外部模块的类型声明文件,需要自己安装
1. ts编译器自动生成
只要使用编译选项declaration
,编译器就会在编译时自动生成单独的类型声明文件。
下面是在tsconfig.json
文件里面,打开这个选项。
{ "compilerOptions": { "declaration": true } }
你也可以在命令行打开这个选项。
$ tsc --declaration
2. 内置声明文件
安装 TypeScript 语言时,会同时安装一些内置的类型声明文件,主要是内置的全局对象(JavaScript 语言接口和运行环境 API)的类型声明。
这些内置文件存放在ts安装目录的lib文件夹内,文件名统一为lib.[description].d.ts
的形式,其中description
部分描述文件内容,比如lib.dom.d.ts
文件就是描述了DOM结构的类型。
ts 编译器会自动根据编译目标target
的值,加载对应的内置声明文件,所以不需要特别的配置。但是,可以使用编译选项lib
,指定加载哪些内置声明文件。
{ "compilerOptions": { "lib": ["dom", "es2021"] } }
上面示例中,lib
选项指定加载dom
和es2021
这两个内置类型声明文件。编译选项noLib
会禁止加载任何内置声明文件
3. 外部类型声明文件
如果项目中使用了外部的某个第三方代码库,就需要这个库的类型声明文件。
这时有分三种情况
- 这个库自带了类型声明文件。
一般来说,如果这个库的源码包含了[vendor].d.ts
文件,那么就自带了类型声明文件。其中的vendor
表示这个库的名字,比如moment
这个库就自带moment.d.ts
。使用这个库可能需要单独加载它的类型声明文件。
- 这个库没有自带,但是可以找到社区制作的类型声明文件
第三方库如果没有提供类型声明文件,社区往往会提供。ts 社区主要使用 DefinitelyTyped 仓库,各种类型声明文件都会提交到那里,已经包含了几千个第三方库。
这些声明文件都会作为一个单独的库,发布到 npm 的@types
名称空间之下。比如,jQuery 的类型声明文件就发布成@types/jquery
这个库,使用时安装这个库就可以了。
$ npm install @types/jquery --save-dev
执行上面的命令,@types/jquery
这个库就安装到项目的node_modules/@types/jquery
目录,里面的index.d.ts
文件就是 jQuery 的类型声明文件。如果类型声明文件不是index.d.ts
,那么就需要在package.json
的types
或typings
字段,指定类型声明文件的文件名。
ts 会自动加载node_modules/@types
目录下的模块,但可以使用编译选项typeRoots
改变这种行为。
{ "compilerOptions": { "typeRoots": ["./typings", "./vendor/types"] } }
上面示例表示,ts 不再去node_modules/@types
目录,而是去跟当前tsconfig.json
同级的typings
和vendor/types
子目录,加载类型模块了。
默认情况下,ts会自动加载typeRoots
目录里的所有模块,编译选项types
可以指定加载哪些模块。
{ "compilerOptions": { "types" : ["jquery"] } }
上面设置中,types
属性是一个数组,成员是所要加载的类型模块,要加载几个模块,这个数组就有几个成员,每个类型模块在typeRoots
目录下都有一个自己的子目录。这样的话,TypeScript 就会自动去jquery
子目录,加载 jQuery 的类型声明文件。
- 找不到类型声明文件,需要自己写
有时实在没有第三方库的类型声明文件,又很难完整给出该库的类型描述,这时你可以告诉 ts 相关对象的类型是any
。比如,使用 jQuery 的脚本可以写成下面这样。
declare var $:any // 或者 declare type JQuery = any; declare var $:JQuery;
上面代码表示,jQuery 的$
对象是外部引入的,类型是any
,也就是 ts 不用对它进行类型检查。
也可以采用下面的写法,将整个外部模块的类型设为any
。
declare module '模块名';
有了上面的命令,指定模块的所有接口都将视为any
类型
3. declare关键字
类型声明文件只包含类型描述,不包含具体实现,所以非常适合使用declare语句来描述类型。
类型声明文件里面,变量的类型描述必须使用declare
命令,否则会报错,因为变量声明语句是值相关代码。
declare let foo:string;
interface 类型有没有declare
都可以,因为 interface 是完全的类型代码。
interface Foo {} // 正确 declare interface Foo {} // 正确
类型声明文件里面,顶层可以使用export
命令,也可以不用,除非使用者脚本会显式使用export
命令输入类型。
export interface Data { version: string; }
4. 模块发布
当前模块如果包含自己的类型声明文件,可以在 package.json 文件里面添加一个types
字段或typings
字段,指明类型声明文件的位置。
{ "name": "awesome", "author": "Vandelay Industries", "version": "1.0.0", "main": "./lib/main.js", "types": "./lib/main.d.ts" }
如果类型声明文件名为index.d.ts
,且在项目的根目录中,那就不需要在package.json
里面注明了。
有时,类型声明文件会单独发布成一个 npm 模块,这时就必须同时加载该模块。
这是一个模块的 package.json 文件,该模块需要 browserify 模块。由于后者的类型声明文件是一个单独的模块@types/browserify
,所以还需要加载那个单独的模块(类型声明是个单独模块)。
{ "name": "browserify-typescript-extension", "author": "Vandelay Industries", "version": "1.0.0", "main": "./lib/main.js", "types": "./lib/main.d.ts", "dependencies": { "browserify": "latest", "@types/browserify": "latest", "typescript": "next" } }
5. 三斜杆命令
如果类型声明文件的内容特别多,可以拆分成多个文件,然后再入口文件使用三斜杆命令去加载拆分后的文件。
举例来说,入口文件是main.d.ts
,里面的接口定义在interfaces.d.ts
,函数定义在functions.d.ts
。那么,main.d.ts
里面可以用三斜杠命令,加载后面两个文件。
// main.d.ts文件 /// <reference path="./interfaces.d.ts" /> /// <reference path="./functions.d.ts" />
三斜杠命令(///
)是一个 ts 编译器命令,用来指定编译器行为。它只能用在文件的头部,如果用在其他地方,会被当作普通的注释。另外,若一个文件中使用了三斜线命令,那么在三斜线命令之前只允许使用单行注释、多行注释和其他三斜线命令,否则三斜杠命令也会被当作普通的注释。
除了拆分类型声明文件,三斜杠命令也可以用于普通脚本加载类型声明文件。
三斜杆命令有三个参数,代表不同的命令。path、types、lib
1. ///<reference path=“”>
告诉编译器在编译时需要包括的文件,来声明当前脚本依赖的类型文件。编译器会在预处理阶段,找出所有三斜杠引用的文件,将其添加到编译列表中,然后一起编译。
/// <reference path="./lib.ts" /> let count = add(1, 2);
上面示例表示,当前脚本依赖于./lib.ts
,里面是add()
的定义。编译当前脚本时,还会同时编译./lib.ts
。编译产物会有两个 JS 文件,一个当前脚本,另一个就是./lib.js
。
path
参数指定了所引入文件的路径。如果该路径是一个相对路径,则基于当前脚本的路径进行计算。
使用该命令时,有以下两个注意事项。
path
参数必须指向一个存在的文件,若文件不存在会报错。path
参数不允许指向当前文件。
默认情况下,每个三斜杠命令引入的脚本,都会编译成单独的 JS 文件。如果希望编译后只产出一个合并文件,可以使用编译选项outFile
。但是,outFile
编译选项不支持合并 CommonJS 模块和 ES 模块,只有当编译参数module
的值设为 None、System 或 AMD 时,才能编译成一个文件。
如果打开了编译参数noResolve
,则忽略三斜杠指令。将其当作一般的注释,原样保留在编译产物中。
2. /// <reference types=“” />
types参数用来告诉编译器当前脚本依赖某个DefinitelyTyped 类型库,通常安装在node_modules/@types
目录。
types 参数的值是类型库的名称,也就是安装到node_modules/@types
目录中的子目录的名字。
/// <reference types="node" />
上面例子中,这个三斜杠命令表示编译时添加 Node.js 的类型库,实际添加的脚本是node_modules
目录里面的@types/node/index.d.ts
。
可以看到,这个命令的作用类似于import
命令。
注意,这个命令只在你自己手写类型声明文件(.d.ts
文件)时,才有必要用到,也就是说,只应该用在.d.ts
文件中,普通的.ts
脚本文件不需要写这个命令。如果是普通的.ts
脚本,可以使用tsconfig.json
文件的types
属性指定依赖的类型库。
3. /// <reference lib=“” />
/// <reference lib="..." />
命令允许脚本文件显式包含内置 lib 库,等同于在tsconfig.json
文件里面使用lib
属性指定 lib 库。
安装 ts 软件包时,会同时安装一些内置的类型声明文件,即内置的 lib 库。这些库文件位于 ts 安装目录的lib
文件夹中,它们描述了 JavaScript 语言和引擎的标准 API。
库文件并不是固定的,会随着 ts 版本的升级而更新。库文件统一使用“lib.[description].d.ts”的命名方式,而/// <reference lib="" />
里面的lib
属性的值就是库文件名的description
部分,比如lib="es2015"
就表示加载库文件lib.es2015.d.ts
。
/// <reference lib="es2017.string" />
上面例子中,es2017.string
对应的库文件就是lib.es2017.string.d.ts
。
到此这篇关于TypeScript中d.ts类型声明文件的实现的文章就介绍到这了,更多相关TypeScript d.ts类型声明文件内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!