我应该如何构建一个包含主线程(DOM)脚本和工作程序的项目?例如:
// This file must have DOM types, but not worker types.
const worker = new Worker('worker.js');
worker.onmessage = (event) => {
// Ideally, I should be able to reference types from the worker:
const data = event.data as import('./worker').HelloMessage;
console.log(data.hello);
};
// This file must have worker types, but not DOM types.
// The global object must be one of the worker globals (how do I pick which?)
const helloMessage = {
hello: 'world',
};
export type HelloMessage = typeof helloMessage;
postMessage(helloMessage);
每当我过去尝试过这种方法时,我都会觉得自己一直在与TypeScript对抗:
tsconfig.json
。但这当然不是准确的类型。tsconfig.json
。但是项目边界使它们之间的类型引用变得困难。此外,如何在worker中声明全局变量?以前我使用过declare var self: DedicatedWorkerGlobalScope
,但是有没有一种方法可以实际设置全局变量(而不仅仅是设置self
)?
答案 0 :(得分:20)
非常感谢Mattias Buelens向我指出了正确的方向。
项目结构为:
dist
src
generic-tsconfig.json
main
tsconfig.json
dedicated-worker
tsconfig.json
service-worker
tsconfig.json
src/generic-tsconfig.json
这包含每个项目通用的配置:
{
"compilerOptions": {
"target": "esnext",
"module": "esnext",
"strict": true,
"moduleResolution": "node",
"rootDir": ".",
"outDir": "../dist",
"composite": true,
"declarationMap": true,
"sourceMap": true
}
}
我故意避免称其为tsconfig.json
,因为它本身不是项目。使以上内容适应您的需求。这是重要的部分:
outDir
-这是转译的脚本,声明和源映射的存放位置。rootDir
-通过将其设置为src
目录,每个子项目(main
,dedicated-worker
,service-worker
)将在子目录中显示为子目录。 outDir
,否则他们将尝试共享同一目录并相互覆盖。composite
-这是TypeScript在项目之间保留引用的必需条件。此文件中请勿包含references
。由于某些未记录的原因,它们将被忽略(这就是我被卡住的地方)。
src/main/tsconfig.json
这是“主线程”项目的配置,例如,可以访问文档的JavaScript。
{
"extends": "../generic-tsconfig.json",
"compilerOptions": {
"lib": ["esnext", "dom"],
},
"references": [
{"path": "../dedicated-worker"},
{"path": "../service-worker"}
]
}
extends
-指向我们上面的通用配置。compilerOptions.lib
-该项目使用的库。在这种情况下,是JS和DOM。references
-由于这是主要项目(我们构建的项目),因此它必须引用所有其他子项目以确保它们也已构建。src/dedicated-worker/tsconfig.json
这是专用工作程序(您使用new Worker()
创建的工作程序)的配置。
{
"extends": "../generic-tsconfig.json",
"compilerOptions": {
"lib": ["esnext", "webworker"],
}
}
除非您从其他子项目中导入内容(例如类型),否则您无需在此处引用其他子项目。
TypeScript不会区分不同的工作程序上下文,尽管它们具有不同的全局变量。因此,事情变得有些混乱:
postMessage('foo');
这是有效的,因为TypeScript的“ webworker”类型为所有专用的工作人员全局变量创建全局变量。但是:
self.postMessage('foo');
…这失败了,因为TypeScript为self
提供了一个不存在的类型,该类型类似于全局的抽象工作器。
要解决此问题,请将其包括在您的来源中
declare var self: DedicatedWorkerGlobalScope;
export {};
这会将self
设置为正确的类型。
除非文件是模块,否则declare var
位将不起作用,并且伪导出使TypeScript将其视为模块。这意味着您要在模块范围中声明self
,该范围目前尚不存在。否则,您要尝试在已经存在的全局位置上声明它。
src/service-worker/tsconfig.json
与上述相同。
{
"extends": "../generic-tsconfig.json",
"compilerOptions": {
"lib": ["esnext", "webworker"],
}
}
如上所述,TypeScript的“ webworker”类型为所有专用工作程序全局变量创建全局变量。但这不是专门的工作人员,因此某些类型不正确:
postMessage('yo');
TypeScript不会抱怨上述问题,但是由于postMessage
不在全局服务工作程序中,因此它在运行时将失败。
很遗憾,您无法采取任何措施来修复实际的全局变量,但是您仍然可以修复self
:
declare var self: ServiceWorkerGlobalScope;
export {};
现在,您需要确保通过self
访问服务工作者专用的每个全局变量。
addEventListener('fetch', (event) => {
// This is a type error, as the global addEventListener
// doesn't know anything about the 'fetch' event.
// Therefore it doesn't know about event.request.
console.log(event.request);
});
self.addEventListener('fetch', (event) => {
// This works fine.
console.log(event.request);
});
对于其他类型的工作程序(例如工作集和共享工作程序),存在相同的问题和解决方法。
tsc --build src/main
就是这样!