+
+
declare 关键字
+
最近在做那个开源之夏 然后要用到这个declare 进行类型检查就正好写一篇啦 (其实还有很多不会的)👼
+
本来想写JSX
的自定义接口来着 但是没准备看懂 所以写了这个
+
简介
+
declare 关键字用来告诉编译器,某个类型是存在的,可以在当前文件中使用🛩️
+
它的主要作用,就是让当前文件可以使用其他文件声明的类型。举例来说,自己的脚本使用外部库定义的函数,编译器会因为不知道外部函数的类型定义而报错,这时就可以在自己的脚本里面使用declare
关键字,告诉编译器外部函数的类型。这样的话,编译单个脚本就不会因为使用了外部类型而报错。
+
declare 关键字可以描述以下类型。
+
+变量(const、let、var 命令声明)
+type 或者 interface 命令声明的类型
+class
+enum
+函数(function)
+模块(module)
+命名空间(namespace)
+
+
declare 关键字的重要特点是,它只是通知编译器某个类型是存在的,不用给出具体实现。比如,只描述函数的类型,不给出函数的实现,如果不使用declare
,这是做不到的。
+
declare 只能用来描述已经存在的变量和数据结构,不能用来声明新的变量和数据结构。另外,所有 declare 语句都不会出现在编译后的文件里面。
+
declare variable
+
declare 关键字可以给出外部变量的类型描述。
+
举例来说,当前脚本使用了其他脚本定义的全局变量x
。
+
+
上面示例中,变量x
是其他脚本定义的,当前脚本不知道它的类型,编译器就会报错。
+
这时使用 declare 命令给出它的类型,就不会报错了。
+
1 declare let x :number ;x = 1 ;
+
如果 declare 关键字没有给出变量的具体类型,那么变量类型就是any
。
+
+
上面示例中,变量x
的类型为any
。
+
下面的例子是脚本使用浏览器全局对象document
。
+
1 declare var document ;document .title = 'Hello' ;
+
上面示例中,declare 告诉编译器,变量document
的类型是外部定义的(具体定义在 TypeScript 内置文件lib.d.ts
)。
+
如果 TypeScript 没有找到document
的外部定义,这里就会假定它的类型是any
。
+
注意,declare 关键字只用来给出类型描述,是纯的类型代码,不允许设置变量的初始值,即不能涉及值。
+
1 2 declare let x :number = 1 ;
+
上面示例中,declare 设置了变量的初始值,结果就报错了。
+
declare function
+
declare 关键字可以给出外部函数的类型描述。
+
1 2 declare function sayHello (name:string ):void ;sayHello ('zzx' );
+
上面示例中,declare 命令给出了sayHello()
的类型描述,因此可以直接使用它。
+
注意,这种单独的函数类型声明语句,只能用于declare
命令后面。一方面,TypeScript 不支持单独的函数类型声明语句;另一方面,declare 关键字后面也不能带有函数的具体实现。
+
1 2 function sayHello ( name:string ):void ;function sayHello (name ) { return '你好,' + name;}
+
上面示例中,单独写函数的类型声明就会报错。
+
declare class
+
declare 给出 class 的描述描述写法如下。
+
1 declare class Animal { constructor (name:string ); eat ():void ; sleep ():void ;}
+
下面是一个复杂一点的例子。
+
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 declare class C { public static s0 ():string ; private static s1 :string ; public a :number ; private b :number ; constructor (arg:number ); m (x :number , y :number ):number ; get c ():number ; set c (value:number ); [index :string ]:any ; }
+
同样的,declare 后面不能给出 Class 的具体实现或初始值。
+
declare module,declare namespace
+
如果想把变量、函数、类组织在一起,可以将 declare 与 module 或 namespace 一起使用。
+
1 2 3 4 5 6 7 8 9 10 11 12 13 14 declare namespace AnimalLib { class Animal { constructor (name:string ); eat ():void ; sleep ():void ; } type Animals = 'Fish' | 'Dog' ;}declare module AnimalLib { class Animal { constructor (name:string ); eat (): void ; sleep (): void ; } type Animals = 'Fish' | 'Dog' ;}
+
这个栗子中 declare 关键字给出了 module 或 namespace 的类型描述。
+
declare module 和 declare namespace 里面,加不加 export 关键字都可以。
+
1 declare namespace Foo { export var a : boolean ;}declare module 'io' { export function readFile (filename:string ):string ;}
+
上面示例中,namespace 和 module 里面使用了 export 关键字。
+
declare 关键字的另一个用途,是为外部模块添加属性和方法时,给出新增部分的类型描述。
+
1 import { Foo as Bar } from 'moduleA' ;declare module 'moduleA' { interface Bar extends Foo { custom : { prop1 :string ; } }}
+
上面示例中,从模块moduleA
导入了Foo
接口,将其重命名为Bar
,并用 declare 关键字为Bar
增加一个属性custom
。
+
下面是另一个例子。一个项目有多个模块,可以在一个模型中,对另一个模块的接口进行类型扩展。
+
1 2 3 4 5 6 7 export interface A { x : number ;}import { A } from './a' ;declare module './a' { interface A { y : number ; }}const a :A = { x : 0 , y : 0 };
+
上面示例中,脚本a.ts
定义了一个接口A
,脚本b.ts
为这个接口添加了属性y
。declare module './a' {}
表示对a.ts
里面的模块,进行类型声明,而同名 interface 会自动合并,所以等同于扩展类型。
+
使用这种语法进行模块的类型扩展时,有两点需要注意:
+
(1)declare module NAME
语法里面的模块名NAME
,跟 import 和 export 的模块名规则是一样的,且必须跟当前文件加载该模块的语句写法(上例import { A } from './a'
)保持一致。
+
(2)不能创建新的顶层类型。也就是说,只能对a.ts
模块中已经存在的类型进行扩展,不允许增加新的顶层类型,比如新定义一个接口B
。
+
(3)不能对默认的default
接口进行扩展,只能对 export 命令输出的命名接口进行扩充。这是因为在进行类型扩展时,需要依赖输出的接口名。
+
某些第三方模块,原始作者没有提供接口类型,这时可以在自己的脚本顶部加上下面一行命令。
+
+
加上上面的命令以后,外部模块即使没有类型,也可以通过编译。但是,从该模块输入的所有接口都将为any
类型。
+
declare module 描述的模块名可以使用通配符。
+
1 2 3 4 5 6 7 8 declare module 'my-plugin-*' { interface PluginOptions { enabled : boolean ; priority : number ; } function initialize (options: PluginOptions ): void ; export = initialize; }
+
上面示例中,模块名my-plugin-*
表示适配所有以my-plugin-
开头的模块名(比如my-plugin-logger
)。
+
+
+
+
+