最近有个需求,须要写声明文件。虽然一直有在用typescript
,可是对声明文件相关信息没有怎么使用过,因而记录一下。javascript
在使用第三方库的时候,想使用typescript
类型检查、自动补全等等功能,须要一个描述javascript
库和模块信息的声明文件。一般来讲,都是将声明语句放到一个单独文件中*.d.ts
。html
对于第三方库,目前也是DefinitelyTyped推荐的两种方式:java
typescript
,那么推荐在包里捆绑自动生成的声明文件。补充:在tsconfig.json
里,能够设置如下属性去自动生成声明文件:
declaration
:设置能够自动生成*.d.ts
声明文件declarationDir
:设置生成的*.d.ts
声明文件的目录declarationMap
:设置生成*.d.ts.map
文件(sourceMap)emitDeclarationOnly
:不生成js
文件,仅仅生成*.d.ts
和*.d.ts.map
typescript
,那么能够选择发起一个PR
给DefinitelyTyped。若是合并到了master
上,会自动发布到npm
上。即:@types/xxx
。*.d.ts
和*.ts
的区别在于:node
*.d.ts
对于typescript
而言,是类型声明文件,且在*.d.ts
文件中的顶级声明必须以declare
或export
修饰符开头。同时在项目编译事后,*.d.ts
文件是不会生成任何代码的。补充:默认使用tsc —init
会开启skipLibCheck
跳过声明文件检查,能够关闭它。*.ts
则没有那么多限制,任何在*.d.ts
中的内容,都可以在*.ts
中使用。同时根据文档,在typescript 2.0
之后,默认全部可见的@types
包,会在编译过程当中包含进来,例如:./node_modules/@types/
、../node_modules/@types/
和../../node_modules/@types/
等等。react
可是,若是指定了typeRoots
或者types
,那么只有typeRoots
目录下的包才会被引入,或者被types
指定的包。git
例如:设置"types": []
会禁用自动引入@types
包的功能。es6
如下语法并不只仅只能在声明文件中,只是说,至关于普通文件书写,更频繁出如今声明文件中。github
在声明文件中,最常看见的语法之一。用来全局声明变量、常量、类、全局对象等等,前提是该文件不是模块声明文件(后面会讲)。typescript
declare const Jye1: string;
declare let Jye2: string;
declare class Jye3 {}
declare namespace Jye4 {}
// ...
复制代码
同时在声明函数的时候,也是支持函数重载的。npm
declare function name(params: string): void;
declare function name(params: number): number;
复制代码
在使用declare
声明类型的时候,并不能去定义具体的实现过程。
比较特别的,像是经过declare global
,能够拓展全局变量的类型和方法。
// ./types/test.d.ts
declare global {
interface String {
helloword(): string;
}
}
export {};
复制代码
// ./src/test.ts
const test = "jye";
test.helloword();
复制代码
若是不加export {}
,会报「全局范围的扩大仅可直接嵌套在外部模块中或环境模块声明中」错误。增长export{}
其实也就是为了让这个声明文件变成模块声明文件,而不是一个全局声明文件。
前言:在typescript 1.5
里,内部模块被称作「命名空间」,外部模块称为「模块」。同时module X {
至关于如今推荐的写法namespace X {
。文档
namespace
一开始的提出,主要是为了模块化(防止命名冲突等等)。可是ES6
普及以后,namespace
已经再也不推荐使用了,更推荐使用ES6
模块化。可是,在声明文件中namespace
比较常见的。
命名空间表示一个全局变量是一个对象,能够定义不少属性类型。同时命名空间里可能会用到一些接口类型(interface
、type
),这时候通常有两种写法:
namespace
外层,会做为全局类型被引入,从而可能污染全局类型空间。namespace
里层,在想使用该类型的时候,能够经过namespace.interface
进行使用。(推荐)// ./types/test.d.ts
declare namespace Jye {
interface Info {
name: string;
age: number;
}
function getAge(): number;
}
复制代码
// ./src/test.ts
let settings: Jye.Info = {
name: "jye",
age: 8,
};
Jye.getAge();
复制代码
同时,命名空间支持嵌套使用,即:namespace
嵌套namepsace
。或者简化的写法,能够写成namepsace.namespace
进行声明。
同时命名空间也支持声明合并。
// ./types/test.d.ts
declare namespace Jye.Eee {
interface Api {
getInfo(): Info;
}
}
复制代码
三斜线指令,也是最初用来表示模块之间依赖关系。目前也是不多会去使用,不过声明文件中,仍是有不少会去使用。
在三斜线指令的语法中,目前可能会去比较经常使用的两种语法:
/// <reference path="./lib/index.d.ts" />
:表示对一个文件的依赖。/// <reference types="jye" />
:表示对一个库的依赖。说白了,三斜线的path
& types
,和es6
的import
语义类似,同时三斜线指令必须放在文件的最顶端。例如,当咱们的声明文件过于庞大,通常都会采用三斜线指令,将咱们的声明文件拆分红若干个,而后由一个入口文件引入。
在配置tsconfig.json
设置declaration
为ture
去自动生成声明文件或者是手动去写声明文件,比较常见的语法,像是:
export
:导出变量export default
: 默认导出export namespace
:导出对象export =
:commonJS导出npm
包的声明文件相对于以前的全局声明文件而言,能够理解为是局部声明文件。只有当经过import
引入npm
包后,才能使用对应的声明类型。而前三个语法,其实和es6
相似,用法语义一目了然。
比较特殊的是,export =
对应的像是import xxx = require
。其实使用都是相似的,只是为了兼容AMD
和commonJS
才有的语法。文档
其实也就是说,对于一个npm
包的声明文件,只有经过export
导出的类型,才能被使用。
其实写到这里,前面有两点没有说清楚,什么是全局声明,什么是局部声明。
个人理解是,若是这个声明文件被typescript
引入了,那么这个文件不包含import
export
,那么这个文件中包含的declare & interface & type
就会变成全局声明。反之,如果这个文件包含了import export
,那么这个文件包含的declare & interface & type
则会是局部声明,不会影响到全局声明。
以@types/react
为例:配置tsconfig.json
关闭自动引入@types
文件,且在@types/react
中增长declare
:
// @types/react/index.d.ts
export = React;
export as namespace React;
declare namespace Jye {
interface Info {
name: string;
age: number;
}
function getAge(): number;
}
复制代码
同时在a
文件import React from 'react
,在b
文件使用相关类型
// src/b.ts
React.Children; // ok
let settings: Jye.Info = { // 找不到命名空间“Jye”。ts(2503)
name: 'jye',
age: 8,
};
Jye.getAge(); // 找不到命名空间“Jye”。ts(2503)
复制代码
能够看到,在项目中引入了react
后,那么该文件导出的类型则被引入到全局中。可是除却export
出来的类型,其余declare
的类型,则没法被使用。
同理,能够在项目中,定义*d.ts
,经过设置export {}
将其从一个全局声明文件变成一个模块声明文件。那么对应declare
内容则会没法使用,只能经过引入文件后,使用其export
出来的类型。
那么总结:若是没有export import
,那么这个文件被引入后,则会是一个全局声明,(也就是说这个文件是全局声明文件)。不然,这个文件被引入后,仅仅其export
的内容,被引入到全局里,其余内容则做为局部声明(这个文件是模块声明文件)。
在项目中,设置package.json
的types
或者typings
指向声明文件。在设置types
或者typings
后,会去找指向的声明文件。若是没有定义,则会去找根目录下的index.d.ts
,再没有则去找入口文件,是否存在对应文件名的声明文件。
具体typescript
如何解析查找模块类型,能够看这篇文章传送门
能够经过如下方法去让typescript
引入类型:
tsconfig.json
配置types
指定咱们的包名。import
手动导入咱们的包。