Typescript: 在Electron 或 Nodejs 的ES6模块中 跨文件 声明全局变量和函数
这几天学着用Typescript开发Electron,遇到了一些问题。
首先,我写了一个utility.ts,里面放着一些公用的函数。
1 // utility.ts 2 function isChinese(c: any): boolean { 3 return c[0] >= '\u4e00' && c[0] <= '\u9fa5'; 4 }
我希望在main.ts里面使用它。
1 // main.ts 2 import * as electron from "electron"; 3 4 // ... 5 // ... 6 7 import { GlobalResource } from"./myResource" 8 var globalResource: GlobalResource; 9 10 function init(){ 11 globalResource = new GlobalResource(); 12 isChinese("人"); 13 }
在main.ts里,声明了globalResource。按照预期,这个全局变量应该可以在任何地方使用,且因为注明了类型,是有代码提示的。
但是,因为我在这个ts文件里使用了import,这个文件被视作一个文件模块,有独立的命名空间,而不是全局。
按照Typescript的设计理念,用户不该污染全局的命名空间,所以鼓励用户把程序分为一个个模块。
如果在一个ts文件的根级别位置(比如main.ts的第二行)出现了import 或 export,这个文件就会被当做一个单独的模块。
那么问题来了,我既希望使用全局变量和函数,又希望有代码提示,那么就必须使用import导入类型所在的ts文件,然后表明类型。如果用 var gr = require("./myResource"); 导入,是没有代码提示的。
所以我需要找到能在模块内使用和声明全局成员的方式。
全局ts文件中添加全局函数或变量
要在一个全局ts文件中添加全局函数或变量,需要如下操作:
1 // utility.ts 2 function isChinese(c: any): boolean { 3 return c[0] >= '\u4e00' && c[0] <= '\u9fa5'; 4 } 5 6 global.isChinese = isChinese;
只要在global实例中把我们希望能在全局访问的函数添加进去,就可以在模块里,通过global.isChinese 的方式访问。
当然,为了让解释器加载utility.ts 文件,需要在main.ts 中写 import "./utility";
不过,这个方法依然无法在模块里声明全局成员。
假如我在main.ts 里写 global.globalResource = globalResource; 编译器会报错:类型“Global & typeof globalThis”上不存在属性“globalResource”。ts(2339)
无法修改其他模块导出的变量
为了能够在模块里声明全局变量,我在网上找了很久。两个小时过去,还是没有进展。于是我放弃了。
globalResource是必然会被其他模块访问的。那么,我干脆写一个专门用于储存全局资源的ts模块好了。
1 // main.ts 2 import * as electron from "electron"; 3 4 // ... 5 // ... 6 7 import { GlobalResource } from"./myResource" 8 import * as MyGlobal from "./myGlobal" 9 10 function init(){ 11 MyGlobal.globalResource = new GlobalResource(); 12 isChinese("人"); 13 } 1 // myGlobal.ts 2 import { GlobalResource } from"./myResource" 3 export var globalResource: GlobalResource;
出了其他问题。
在main.ts 的第14行,编辑器提示我不能修改一个readonly 变量。但globalResource分明不是readonly。
这是因为对于其他模块来说,export出来的变量是只读的。只有那个模块内部才可以对这个变量进行操作。
readonly也算是合理。如果有多个模块都使用这个变量,且可以修改它,可能会让其他使用这个变量的模块出现问题。
虽说我确实可以把globalResource的初始化 放到 myGlobal.ts 这个文件里,但是和我预先的计划出现了偏差。我是打算把 myGlobal.ts 仅仅当做一个放变量的容器,把那些初始化的操作都放在 main.ts 里。
所以我还是决定浪费一点时间,找办法在模块里声明全局变量。
在模块里声明全局变量
这种方法看起来不简洁,但确实是有用的。
1 // main.ts 2 import * as electron from "electron"; 3 4 // ... 5 // ... 6 7 import { GlobalResource } from "./myResource" 8 9 declare global { 10 namespace NodeJS { 11 interface Global { 12 globalResource: GlobalResource; 13 } 14 } 15 } 16 17 function init(){ 18 global.globalResource = new GlobalResource(); 19 isChinese("人"); 20 }
在其他模块里,可以直接用global.globalResource 访问和修改这个全局变量。
大概研究了一下这个办法。
第9行的 declare global 是为了把大括号里面的东西添加进全局namespace。
第10和11行 可以参照一下 ./node_modules/@types/node/globals.d.ts 这个文件。里面声明了namespace Nodejs 以及 interface Global。
相当于把这个全局变量加进了globals.d.ts 里。
参考:
https://segmentfault.com/q/1010000015952325
https://www.typescriptlang.org/docs/handbook/declaration-files/templates/global-modifying-module-d-ts.html

浙公网安备 33010602011771号