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

posted @ 2021-02-21 13:39  枭尘-  阅读(1417)  评论(0)    收藏  举报