Typescript 的类型声明可以扩展全局和第三方库的现有类型。第三方库自己拥有类型说明或在 @types 包中有类型说明,但有时第三方库没有提供累心声明或者类型不满足我们的要求,就需要我们自己编写类型声明。
大体来说,类型声明分为全局和模块,分别有不同的使用场景和方法。
全局声明指写在不包含export
的文件中的类型声明(不限于.d.ts
文件,但我们通常使用这个后缀),全局声明被编译器自动检测,自动生效。
模块声明指写在包含export
的文件中的类型声明,需要被引入后才能生效。
声明全局变量
全局变量需要在全局声明文件中声明,比如常用的 global.d.ts 文件:
declare let a: number
这样在其它地方(全局或模块),就可以使用声明的变量,比如:
a = 1
需要注意的是,声明全局变量时可以赋值:declare let a = 0
,但这里的赋值没有实际效果,只是能给编译器进行类型推断,此时a
的实际值为undefined
。
如果需要声明的变量是一个对象,那么可以使用namespace
关键词:
declare namespace a {
let x: number
}
等效于:
declare let f: {
x: number
y: number
}
但namespace
的不同之处在于其可以通过export
来使这个变量称谓一个模块,比如:
declare namespace a {
export { x } from 'x'
}
此时声明的a
就是一个包含成员x
的对象,需要注意的是,此时a
变成了一个模块,这个 namespace 内部使用 let
声明的变量不会被导出,就像是普通的模块那样。
扩展模块
对于 node_modules 中的第三方库,或是别人写的模块不方便直接修改代码的,可以通过扩展模块的方式声明额外变量或成员。
示例:
a.ts
interface A {
x: number
}
export { A }
index.ts
import { A } from 'a'
declare module 'a' {
interface A {
y?: number
}
}
let a = { x: 0, y: 0 }
export { a }
通过关键词declare module
来扩展模块。需要注意的是,扩展模块只能在模块中声明。也就是说,如果再全局声明中使用declare module
,会被识别为对这个模块的整体声明,直接覆盖原本的声明。
添加类型声明
有时候第三方库没有提供声明文件,我们可以在全局声明中为其添加声明。
示例:
declare module 'a' {
export interface A {
x: number
}
}
也可以不需要添加实质内容,此时模块会被识别为any
,比如:
declare module 'a'