在使用第三方库的时候,想使用
typescript
类型检查、自动补全等等功能,需要一个描述
javascript
库和模块信息的声明文件。通常来说,都是将声明语句放到一个单独文件中
*.d.ts
。
对于第三方库,目前也是
DefinitelyTyped
推荐的两种方式:
如果是开发者,且使用的也是
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
的区别在于:
*.d.ts
对于
typescript
而言,是类型声明文件,且在
*.d.ts
文件中的顶级声明必须以
declare
或
export
修饰符开头。同时在项目编译过后,
*.d.ts
文件是不会生成任何代码的。补充:默认使用
tsc —init
会开启
skipLibCheck
跳过声明文件检查,可以关闭它。
而
*.ts
则没有那么多限制,任何在
*.d.ts
中的内容,均可以在
*.ts
中使用。
自动引入@types
同时根据
文档
,在
typescript 2.0
以后,默认所有可见的
@types
包,会在编译过程中包含进来,例如:
./node_modules/@types/
、
../node_modules/@types/
和
../../node_modules/@types/
等等。
但是,如果指定了
typeRoots
或者
types
,那么只有
typeRoots
目录下的包才会被引入,或者被
types
指定的包。
例如:设置
"types": []
会禁用自动引入
@types
包的功能。
声明文件实现
以下语法并不仅仅只能在声明文件中,只是说,相当于普通文件书写,更频繁出现在声明文件中。
declare
在声明文件中,最常看见的语法之一。用来全局声明变量、常量、类、全局对象等等,前提是该文件不是模块声明文件(后面会讲)。
declare const Jye1: string;
declare let Jye2: string;
declare class Jye3 {}
declare namespace Jye4 {}
同时在声明函数的时候,也是支持函数重载的。
declare function name(params: string): void;
declare function name(params: number): number;
在使用declare
声明类型的时候,并不能去定义具体的实现过程。
比较特别的,像是通过declare global
,可以拓展全局变量的类型和方法。
declare global {
interface String {
helloword(): string;
export {};
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
进行使用。(推荐)
declare namespace Jye {
interface Info {
name: string;
age: number;
function getAge(): number;
let settings: Jye.Info = {
name: "jye",
age: 8,
Jye.getAge();
同时,命名空间支持嵌套使用,即:namespace
嵌套namepsace
。或者简化的写法,可以写成namepsace.namespace
进行声明。
同时命名空间也支持声明合并。
declare namespace Jye.Eee {
interface Api {
getInfo(): Info;
三斜线指令
三斜线指令,也是最初用来表示模块之间依赖关系。目前也是很少会去使用,不过声明文件中,还是有很多会去使用。
在三斜线指令的语法中,目前可能会去比较常用的两种语法:
/// <reference path="./lib/index.d.ts" />
:表示对一个文件的依赖。
/// <reference types="jye" />
:表示对一个库的依赖。
说白了,三斜线的path
& types
,和es6
的import
语义相似,同时三斜线指令必须放在文件的最顶端。例如,当我们的声明文件过于庞大,一般都会采用三斜线指令,将我们的声明文件拆分成若干个,然后由一个入口文件引入。
npm包捆绑的声明文件语法
在配置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
:
export = React;
export as namespace React;
declare namespace Jye {
interface Info {
name: string;
age: number;
function getAge(): number;
同时在a
文件import React from 'react
,在b
文件使用相关类型
React.Children;
let settings: Jye.Info = {
name: 'jye',
age: 8,
Jye.getAge();
可以看到,在项目中引入了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
手动导入我们的包。
在项目中,通过三斜线指令引用。
www.typescriptlang.org/
jkchao.github.io/typescript-…
ts.xcatliu.com/