Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 3028d98

Browse files
committed
发布声明文件
1 parent cbec593 commit 3028d98

File tree

13 files changed

+232
-26
lines changed

13 files changed

+232
-26
lines changed

.prettierignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,4 @@ assets
1515
package-lock.json
1616

1717
examples/declaration-files/19-export-default-enum-error/types/foo/index.d.ts
18+
examples/**/*.js

basics/declaration-files.md

Lines changed: 163 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,6 @@ jQuery('#foo');
7878

7979
```plain
8080
/path/to/project
81-
├── README.md
8281
├── src
8382
| ├── index.ts
8483
| └── jQuery.d.ts
@@ -111,12 +110,12 @@ npm install @types/jquery --save-dev
111110

112111
库的使用场景主要有以下几种:
113112

114-
- 全局变量:通过 `<script>` 标签引入第三方库,注入全局变量
115-
- npm 包:通过 `import foo from 'foo'` 导入,符合 ES6 模块规范
116-
- UMD 库:既可以通过 `<script>` 标签引入,又可以通过 `import` 导入
117-
- 直接扩展全局变量:通过 `<script>` 标签引入后,改变一个全局变量的结构
118-
- 在 npm 包或 UMD 库中扩展全局变量:引用 npm 包或 UMD 库后,改变一个全局变量的结构
119-
- 模块插件:通过 `<script>``import` 导入后,改变另一个模块的结构
113+
- [全局变量](#quan-ju-bian-liang):通过 `<script>` 标签引入第三方库,注入全局变量
114+
- [npm 包](#npm-bao):通过 `import foo from 'foo'` 导入,符合 ES6 模块规范
115+
- [UMD 库](#umd-ku):既可以通过 `<script>` 标签引入,又可以通过 `import` 导入
116+
- [直接扩展全局变量](#zhi-jie-kuo-zhan-quan-ju-bian-liang):通过 `<script>` 标签引入后,改变一个全局变量的结构
117+
- [在 npm 包或 UMD 库中扩展全局变量](#zai-npm-bao-huo-umd-ku-zhong-kuo-zhan-quan-ju-bian-liang):引用 npm 包或 UMD 库后,改变一个全局变量的结构
118+
- [模块插件](#mo-kuai-cha-jian):通过 `<script>``import` 导入后,改变另一个模块的结构
120119

121120
### 全局变量
122121

@@ -126,7 +125,6 @@ npm install @types/jquery --save-dev
126125

127126
```plain
128127
/path/to/project
129-
├── README.md
130128
├── src
131129
| ├── index.ts
132130
| └── jQuery.d.ts
@@ -137,12 +135,12 @@ npm install @types/jquery --save-dev
137135

138136
全局变量的声明文件主要有以下几种语法:
139137

140-
- `declare var` 声明全局变量
141-
- `declare function` 声明全局方法
142-
- `declare class` 声明全局类
143-
- `declare enum` 声明全局枚举类型
144-
- `declare namespace` 声明(含有子属性的)全局对象
145-
- `interface``type` 声明全局类型
138+
- [`declare var`](#declare-var) 声明全局变量
139+
- [`declare function`](#declare-function) 声明全局方法
140+
- [`declare class`](#declare-class) 声明全局类
141+
- [`declare enum`](#declare-enum) 声明全局枚举类型
142+
- [`declare namespace`](#declare-namespace) 声明(含有子属性的)全局对象
143+
- [`interface``type`](#interface-he-type) 声明全局类型
146144

147145
#### `declare var`
148146

@@ -494,7 +492,6 @@ jQuery.ajax('/api/get_something');
494492

495493
```plain
496494
/path/to/project
497-
├── README.md
498495
├── src
499496
| └── index.ts
500497
├── types
@@ -521,14 +518,14 @@ jQuery.ajax('/api/get_something');
521518

522519
注意 `module` 配置可以有很多种选项,不同的选项会影响模块的导入导出模式。这里我们使用了 `commonjs` 这个最常用的选项,后面的教程也都默认使用的这个选项。
523520

524-
不管采用了以上两种方式中的哪一种,我都*强烈建议*大家将书写好的声明文件(通过给原作者发 pull request,或者直接提交到 `@types` 里)发布到开源社区中,享受了这么多社区的优秀的资源,就应该在力所能及的时候给出一些回馈。只有所有人都参与进来,才能让 ts 社区更加繁荣。
521+
不管采用了以上两种方式中的哪一种,我都**强烈建议**大家将书写好的声明文件(通过给第三发库发 pull request,或者直接提交到 `@types` 里)发布到开源社区中,享受了这么多社区的优秀的资源,就应该在力所能及的时候给出一些回馈。只有所有人都参与进来,才能让 ts 社区更加繁荣。
525522

526523
npm 包的声明文件主要有以下几种语法:
527524

528-
- `export` 导出变量
529-
- `export namespace` 导出(含有子属性的)全局对象
530-
- `export default` ES6 默认导出
531-
- `export =` commonjs 导出模块
525+
- [`export`](#export) 导出变量
526+
- [`export namespace`](#export-namespace) 导出(含有子属性的)对象
527+
- [`export default`](#export-default) ES6 默认导出
528+
- [`export =`](#export-1) commonjs 导出模块
532529

533530
#### `export`
534531

@@ -999,21 +996,163 @@ export = jQuery;
999996

1000997
除了这两种三斜线指令之外,还有其他的三斜线指令,比如 `/// <reference no-default-lib="true"/>`, `/// <amd-module />` 等,但它们都是废弃的语法,故这里就不介绍了,详情可见[官网](http://www.typescriptlang.org/docs/handbook/triple-slash-directives.html)
1001998

1002-
### 最佳实践
999+
### 自动生成声明文件
1000+
1001+
如果库的源码本身就是由 ts 写的,那么在使用 `tsc` 脚本将 ts 编译为 js 的时候,添加 `declaration` 选项,就可以同时也生成 `.d.ts` 声明文件了。
1002+
1003+
我们可以在命令行中添加 `--declaration`(简写 `-d`),或者在 `tsconfig.json` 中添加 `declaration` 选项。这里以 `tsconfig.json` 为例:
1004+
1005+
```json
1006+
{
1007+
"compilerOptions": {
1008+
"module": "commonjs",
1009+
"outDir": "lib",
1010+
"declaration": true,
1011+
}
1012+
}
1013+
```
1014+
1015+
上例中我们添加了 `outDir` 选项,将 ts 文件的编译结果输出到 `lib` 目录下,然后添加了 `declaration` 选项,设置为 `true`,表示将会由 ts 文件自动生成 `.d.ts` 声明文件,也会输出到 `lib` 目录下。
1016+
1017+
运行 `tsc` 之后,目录结构如下[<sup>30</sup>](https://github.com/xcatliu/typescript-tutorial/tree/master/examples/declaration-files/30-auto-d-ts)
10031018

1004-
TODO
1019+
```plain
1020+
/path/to/project
1021+
├── lib
1022+
| ├── bar
1023+
| | ├── index.d.ts
1024+
| | └── index.js
1025+
| ├── index.d.ts
1026+
| └── index.js
1027+
├── src
1028+
| ├── bar
1029+
| | └── index.ts
1030+
| └── index.ts
1031+
├── package.json
1032+
└── tsconfig.json
1033+
```
1034+
1035+
在这个例子中,`src` 目录下有两个 ts 文件,分别是 `src/index.ts``src/bar/index.ts`,它们被编译到 `lib` 目录下的同时,也会生成对应的两个声明文件 `lib/index.d.ts``lib/bar/index.d.ts`。它们的内容分别是:
1036+
1037+
```ts
1038+
// src/index.ts
1039+
1040+
export * from './bar';
10051041

1006-
### 发布声明文件
1042+
export default function foo() {
1043+
return 'foo';
1044+
}
1045+
```
1046+
1047+
```ts
1048+
// src/bar/index.ts
10071049

1008-
TODO
1050+
export function bar() {
1051+
return 'bar';
1052+
}
1053+
```
1054+
1055+
```ts
1056+
// lib/index.d.ts
1057+
1058+
export * from './bar';
1059+
export default function foo(): string;
1060+
```
1061+
1062+
```ts
1063+
// lib/bar/index.d.ts
1064+
1065+
export declare function bar(): string;
1066+
```
1067+
1068+
可见,自动生成的声明文件基本保持了源码的结构,而将具体实现去掉了,生成了对应的类型声明。
1069+
1070+
使用 `tsc` 自动生成声明文件时,每个 ts 文件都会对应一个 `.d.ts` 声明文件。这样的好处是,使用方不仅可以在使用 `import foo from 'foo'` 导入默认的模块时获得类型提示,还可以在使用 `import bar from 'foo/lib/bar'` 导入一个子模块时,也获得对应的类型提示。
1071+
1072+
除了 `declaration` 选项之外,还有几个选项也与自动生成声明文件有关,这里只简单列举出来,不做详细演示了:
1073+
1074+
- `declarationDir` 设置生成 `.d.ts` 文件的目录
1075+
- `declarationMap` 对每个 `.d.ts` 文件,都生成对应的 `.d.ts.map`(sourcemap)文件
1076+
- `emitDeclarationOnly` 仅生成 `.d.ts` 文件,不生成 `.js` 文件
1077+
1078+
## 发布声明文件
1079+
1080+
当我们为一个库写好了声明文件之后,下一步就是将它发布出去了。
1081+
1082+
此时有两种方案:
1083+
1084+
1. 将声明文件和源码放在一个仓库中
1085+
2. 将声明文件发布到 `@types`
1086+
1087+
这两种方案中优先选择第一种方案。保持声明文件与源码在一个仓库中,使用时就不需要额外增加单独的声明文件库的依赖了,而且也能保证声明文件的版本与源码的版本保持一致。
1088+
1089+
仅当我们在给别人的仓库添加类型声明文件,但原作者不愿意合并 pull request 时,才需要使用第二种方案,将声明文件发布到 `@types` 下。
1090+
1091+
### 将声明文件和源码放在一个仓库中
1092+
1093+
如果声明文件是通过 `tsc` 自动生成的,那么无需做任何其他配置,只需要把编译好的文件也发布到 npm 上,使用方就可以获取到类型提示了。
1094+
1095+
如果是手动写的声明文件,那么需要满足以下条件之一,才能被正确的识别:
1096+
1097+
-`package.json` 中的 `types``typings` 字段指定一个类型声明文件地址
1098+
- 在项目根目录下,编写一个 `index.d.ts` 文件
1099+
- 针对入口文件(`package.json` 中的 `main` 字段指定的入口文件),编写一个同名不同后缀的 `.d.ts` 文件
1100+
1101+
第一种方式是给 `package.json` 中的 `types``typings` 字段指定一个类型声明文件地址。比如:
1102+
1103+
```json
1104+
{
1105+
"name": "foo",
1106+
"version": "1.0.0",
1107+
"main": "lib/index.js",
1108+
"types": "foo.d.ts",
1109+
}
1110+
```
1111+
1112+
指定了 `types``foo.d.ts` 之后,导入此库的时候,就会去找 `foo.d.ts` 作为此库的类型声明文件了。
1113+
1114+
`typings``types` 一样,只是另一种写法。
1115+
1116+
如果没有指定 `types``typings`,那么就会在根目录下寻找 `index.d.ts` 文件,将它视为此库的类型声明文件。
1117+
1118+
如果没有找到 `index.d.ts` 文件,那么就会寻找入口文件(`package.json` 中的 `main` 字段指定的入口文件)是否存在对应同名不同后缀的 `.d.ts` 文件。
1119+
1120+
比如 `package.json` 是这样时:
1121+
1122+
```json
1123+
{
1124+
"name": "foo",
1125+
"version": "1.0.0",
1126+
"main": "lib/index.js"
1127+
}
1128+
```
1129+
1130+
就会先识别 `package.json` 中是否存在 `types``typings` 字段。发现不存在,那么就会寻找是否存在 `index.d.ts` 文件。如果还是不存在,那么就会寻找是否存在 `lib/index.d.ts` 文件。假如说连 `lib/index.d.ts` 都不存在的话,就会被认为是一个没有提供类型声明文件的库了。
1131+
1132+
有的库为了支持导入子模块,比如 `import bar from 'foo/lib/bar'`,就需要额外再编写一个类型声明文件 `lib/bar.d.ts` 或者 `lib/bar/index.d.ts`,这与自动生成声明文件类似,一个库中同时包含了多个类型声明文件。
1133+
1134+
### 将声明文件发布到 `@types`
1135+
1136+
如果我们是在给别人的仓库添加类型声明文件,但原作者不愿意合并 pull request,那么就需要将声明文件发布到 `@types` 下。
1137+
1138+
与普通的 npm 模块不同,`@types` 是统一由 [DefinitelyTyped][] 管理的。要将声明文件发布到 `@types` 下,就需要给 [DefinitelyTyped][] 创建一个 pull-request,其中包含了类型声明文件,测试代码,以及 `tsconfig.json` 等。
1139+
1140+
pull-request 需要符合它们的规范,并且通过测试,才能被合并,稍后就会被自动发布到 `@types` 下。
1141+
1142+
[DefinitelyTyped][] 中创建一个新的类型声明,需要用到一些工具,[DefinitelyTyped][] 的文档中已经有了[详细的介绍](https://github.com/DefinitelyTyped/DefinitelyTyped#create-a-new-package),这里就不赘述了,以官方文档为准。
1143+
1144+
如果大家有此类需求,可以参考下笔者[提交的 pull-request](https://github.com/DefinitelyTyped/DefinitelyTyped/pull/30336/files)
10091145

10101146
## 参考
10111147

10121148
- [Writing Declaration Files](http://www.typescriptlang.org/docs/handbook/writing-declaration-files.html)[中文版](https://zhongsp.gitbooks.io/typescript-handbook/content/doc/handbook/declaration%20files/Introduction.html)
10131149
- [Triple-Slash Directives](http://www.typescriptlang.org/docs/handbook/triple-slash-directives.html)[中文版](https://zhongsp.gitbooks.io/typescript-handbook/content/doc/handbook/Triple-Slash%20Directives.html)
10141150
- [typeRoots or paths](https://github.com/Microsoft/TypeScript/issues/22217#issuecomment-369783776)
1151+
- [DefinitelyTyped][]
10151152

10161153
---
10171154

10181155
- [上一章:类型断言](type-assertion.md)
10191156
- [下一章:内置对象](built-in-objects.md)
1157+
1158+
[DefinitelyTyped]: https://github.com/DefinitelyTyped/DefinitelyTyped/

examples/declaration-files/28-triple-slash-directives/package-lock.json

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

examples/declaration-files/29-triple-slash-directives-global/package-lock.json

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export declare function bar(): string;
2+
//# sourceMappingURL=index.d.ts.map
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
"use strict";
2+
exports.__esModule = true;
3+
function bar() {
4+
return 'bar';
5+
}
6+
exports.bar = bar;
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export * from './bar';
2+
export default function foo(): string;
3+
//# sourceMappingURL=index.d.ts.map
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
"use strict";
2+
function __export(m) {
3+
for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p];
4+
}
5+
exports.__esModule = true;
6+
__export(require("./bar"));
7+
function foo() {
8+
return 'foo';
9+
}
10+
exports["default"] = foo;

examples/declaration-files/30-auto-d-ts/package-lock.json

Lines changed: 14 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
{
2+
"name": "30-auto-d-ts",
3+
"version": "1.0.0",
4+
"description": "",
5+
"main": "lib/index.js",
6+
"scripts": {
7+
"start": "tsc -w",
8+
"build": "tsc",
9+
"test": "echo \"Error: no test specified\" && exit 1"
10+
},
11+
"author": "",
12+
"license": "MIT",
13+
"devDependencies": {
14+
"typescript": "^3.4.5"
15+
}
16+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export function bar() {
2+
return 'bar';
3+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
export * from './bar';
2+
3+
export default function foo() {
4+
return 'foo';
5+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"compilerOptions": {
3+
"module": "commonjs",
4+
"outDir": "lib",
5+
"declaration": true
6+
}
7+
}

0 commit comments

Comments
 (0)