给Window下增加方法
比如我们想在window对象下增加一个halo方法,然后马上调用,会编译器报错:
window.halo = (name: string) => console.log('hello ' + name);
window.halo('yefei'); // Property 'halo' does not exist on type 'Window & typeof globalThis'.ts(2339)
通过在同名的 .d.ts
文件中增加声明,在.tsx
文件中增加实现来实现
// index.d.ts
interface Window {
halo: (name: string) => void
}
// index.tsx
window.halo = (name: string) => { console.log('halo ' + name);}
window.halo('yefei');
注:关于声明文件,虽然使用 index.d.ts 后,在VSCode中不会出现提示语法错误了,但在编译器的命令行仍旧会提示找不到的错误,这是因为:
Please note that the compiler does not include files that can be possible outputs; e.g. if the input includes index.ts, then index.d.ts and index.js are excluded. In general, having files that differ only in extension next to each other is not recommended.
“声明文件不能有同名的ts文件,因为会当作是从ts到d.ts自动生成的文件而忽略”。将名字改为 global.d.ts即可。
给Window下增加类的对象
假定我们要实现一个完整的 EventEmitter的逻辑,会是怎样呢
// event-emitter.ts
class EventEmitter {
m = {}
listen(name, handler) {
this.m[name] = this.m[name] || []
this.m[name].push(handler)
}
emit(name, data) {
if (this.m[name]) {
this.m[name].forEach((h) => {
h(data)
})
}
}
}
export default EventEmitter
// global.d.ts
interface EventEmitter {
listen(name: string, callback: Function)
emit(name: string, param: any)
}
interface Window {
mb: typeof EventEmitter
}
这里我们可以看到,global.d.ts
给mb声明的是一个结构描述,在实际代码中,哪怕你没有往window.mb赋一个 new EventEmitter()
对象,TS也无法检测出来。
编译器只管知道Window下与定义了什么方法,你调用未定义的、或类型不符合的时候,会给到你提示。
局部模块通过global提升
在上例中,EventEmitter是自定义的,没有引用外部模块;如果它来自npm安装和import引用,那么Window会成为一个局部模块。需要需要通过declare lobal
来提升为全局声明。那么写法会变成下面这样:
// global.d.ts
import EventEmitter from 'eventemitter3'
declare global {
interface Window {
mb: EventEmitter
}
}
补充说明:最低成本地声明方法、类实例的方式,当然是 mb: any
,应急使用可以,但是不可取。
stackoverflow:如果所在文件没有import/export,它就是一个全局模块;否则 需要使用declare global语法。