在学如何书写声明文件之前,我们先来看看声明相关的一些东西。

当我们多次使用 interface 定义的时候,会合并接口

这里报错的原因是,我们并没有完全的实现 A 接口。

错误提示告诉我们,还有一个 age 属性没有。

声明合并 - 图1

假如你使用的 2.1 版本的 ts,那么你可以用 keyof 关键字拿到 A 的所有属性值类型。

命名空间的合并

此时必须要导出,不导出哪怕合并了,外面也访问不到。

真相只有一个,如下图。

命名空间与其他的合并。

命名空间与类,实现内部类。

声明合并 - 图2

此时有一个有意思的小问题

你会发现实例化后的 label 并不能访问它的 id 属性。

而直接访问却可以。

声明合并 - 图3

so? 哪出了问题?💻是不是骗我?

别着急,我们来看一看类型。

声明合并 - 图4

假如你认为 typeof 出问题了,于是你会花大量的时间去搜索关于它的资料。

其然不是,而是它们本身就不一样。

表示的是Album.AlbumLabel的实例,我们此时没有声明任何实例属性。

此时我们加一个示例属性。

声明合并 - 图5

从上面的例子我们可以看出,假如想让它指向的是一个带有构造器的类,而不是实例,我们可以用 typeof。

同样还可以给方法上面添加一些东西,比如静态属性。

声明合并 - 图6

还可以跟枚举配合

这里有一个缺陷就是得到的 yellow 是一个 number,得出来之后,就无法知道具体是由哪些颜色混合而成的了。

新建 ob.ts 文件

新建 map.ts 文件

  1. import { Observable } from "./ob";
  2. declare module "./ob" {
  3. interface Observable<T> {
  4. map<U>(f: (x: T) => U): Observable<U>;
  5. }
  6. Observable.prototype.map = function (f) {
  7. let rets = f();
  8. return new Observable<typeof rets>();
  9. }
  10. let o: Observable<number> = new Observable();

声明合并 - 图7

原理就是往原型上面挂载一些方法,其他细节,之前内容都有提到过,不懂的需要复习一下前面的知识。

温故而知新。

假如你想往全局上面扩展方法,你可以这样做。

把你想要扩展的写到declare global里面。

  1. // observable.ts
  2. export class Observable<T> {
  3. // ... still no implementation ...
  4. }
  5. declare global {
  6. interface Array<T> {
  7. toObservable(): Observable<T>;
  8. }
  9. Array.prototype.toObservable = function () {
  10. // ...
  11. }

识别全局变量

就像 JQuery 那样,在浏览器中全局就可以访问的对象。通常我们会使用 namespace,好处就是防止命名冲突。

通常全局变量在源码中会有如下特性

  • 顶级的var语句或function声明
  • 挂载变量到 window 上

声明可能会像这样

当然可能跟官方不一样,咱先暂时这样。

识别模块化库

所有需要 import require 的都是模块库。

假如依赖是的是全局库

一种方式是通过指令包含,假如我们每一个文件都写一个这个,这样会非常的烦,所以你可以去 tsconfig 里面去配置,分别是指定文件,typeRoots指定目录,选择一样即可。

所有 d.ts 文件里面声明,都需要加上declare,d.ts 只能声明,不能有任何默认值。

声明合并 - 图8

编译器告诉我们,该上下文不允许初始化。

定义一个全局变量 version 如下

  1. declare let version : number;
  1. declare function func(name: string): number;

定义一个全局对象下面具有某些属性

之前在说 namespace 的时候,有提到过,其实 namespace 就是对象。

声明合并 - 图9

使用接口和函数重载

在定义文件里面已经可以使用接口,因为接口也是描述类型的一种。

定义类

声明合并 - 图10

其实这些都不难,就是加上 declare,且只写描述类型不写具体实现而已。

回调的可选参数

对于定义回调的可选参数,有一点你必须要注意。

对于传入的回调 done 我们可以选择不要第二个参数,因为回调允许抛弃一些参数,这样是不会报错的,而假如我们在 getObject 方法里面少传一个参数就会报错。

声明合并 - 图11

编译器告诉我们类型不匹配。

定义参数重载的注意事项

应该从小范围到大范围,如下。

  1. declare function fn(x: HTMLDivElement): string;
  2. declare function fn(x: HTMLElement): number;
  3. declare function fn(x: any): any;

声明合并 - 图12

假如你这样写,对其他人来说,开发体验不友好,刚开始就搞那么大的新闻,而且 ts 匹配到第一个就直接调用了,也就是说这样写,基本上全调用的(x:any)

我们之前是不是说过类型合并,我们可以用到这里来。

  1. export var Bar: { a: Bar };
  2. export interface Bar {
  3. count: number;
  4. }

这里的 Bar 就合并成了 var Bar = { a: Bar, count: number },

你可以认为 interface Bar 为该对象添加了一个 count 属性。

此时 Bar 可以当做类型,也就是接口所描述的那样,有一个 count

也可以当做对象。

此时只有只有 count 属性,是因为它的类型是被 interface Bar描述的。

讲道理可以当类型,又可以当值,这种开发姿势非常魔性,不懂它的人只能吐血了,或许当你遇到什么变态的需求的时候,可能需要它。

关于导出

我不管你从哪知道的 语法,别用,你会发现我从来没有提到这个东西,是因为它已经快被淘汰了,尽可能的使用export default something