问题描述
我应该如何构建一个包含主线程 (DOM) 脚本和工作线程的项目?例如:
How should I structure a project that includes main thread (DOM) script, and workers? Eg:
// 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);
};
worker.ts
// 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 抗争:
Whenever I've tried this in the past I feel like I've been fighting TypeScript by either:
- 使用一个
tsconfig.json
同时具有 worker 和DOM 类型.但这当然不是准确的类型. - 使用多个
tsconfig.json
.但是项目边界使得它们之间很难引用类型.
- Using one
tsconfig.json
that has both worker & DOM types. But of course this isn't accurate type-wise. - Using multiple
tsconfig.json
. But then the project boundary makes it hard to reference types between them.
此外,我如何在工作人员中声明全局?以前我使用过 declare var self: DedicatedWorkerGlobalScope
,但是有没有办法实际设置全局(而不仅仅是设置 self
)?
Additionally, how do I declare the global in a worker? Previously I've used declare var self: DedicatedWorkerGlobalScope
, but is there a way to actually set the global (rather than just setting self
)?
推荐答案
非常感谢 Mattias Buelens 指出我朝着正确的方向前进.
Many thanks to Mattias Buelens who pointed me in the right direction.
项目结构为:
dist
src
generic-tsconfig.json
main
- (打字稿文件)
tsconfig.json
- (打字稿文件)
tsconfig.json
- (打字稿文件)
tsconfig.json
这包含每个项目通用的配置:
This contains the config common to each project:
{ "compilerOptions": { "target": "esnext", "module": "esnext", "strict": true, "moduleResolution": "node", "rootDir": ".", "outDir": "../dist", "composite": true, "declarationMap": true, "sourceMap": true } }
我故意避免调用这个
tsconfig.json
,因为它本身不是一个项目.根据您的需要调整上述内容.以下是重要部分:I've deliberately avoided calling this
tsconfig.json
, as it isn't a project itself. Adapt the above to your needs. Here are the important parts:outDir
- 这是转译后的脚本、声明和源映射所在的位置.rootDir
- 通过将其设置为src
目录,每个子项目(main
、dedicated-worker
>,service-worker
) 将作为outDir
中的子目录出现,否则它们将尝试共享同一目录并相互覆盖.composite
- 这是 TypeScript 在项目之间保持引用所必需的.
outDir
- This is where the transpiled script, declarations and source maps will go.rootDir
- By setting this to thesrc
directory, each of the subprojects (main
,dedicated-worker
,service-worker
) will appear as subdirectories in theoutDir
, otherwise they'll try and share the same directory and overwrite each other.composite
- This is required for TypeScript to keep references between projects.
不要在此文件中包含
references
.他们会因为一些无证的原因而被忽略(这就是我被卡住的地方).Do not include
references
in this file. They'll be ignored for some undocumented reason (this is where I got stuck).这是主线程"项目的配置,例如可以访问文档的 JavaScript.
This is the config for the 'main thread' project, As in, the JavaScript that will have access to the document.
{ "extends": "../generic-tsconfig.json", "compilerOptions": { "lib": ["esnext", "dom"], }, "references": [ {"path": "../dedicated-worker"}, {"path": "../service-worker"} ] }
extends
- 这指向我们上面的通用配置.compilerOptions.lib
- 此项目使用的库.在这种情况下,JS 和 DOM.references
- 由于这是主项目(我们构建的项目),它必须引用所有其他子项目以确保它们也被构建.extends
- This points to our generic config above.compilerOptions.lib
- The libs used by this project. In this case, JS and the DOM.references
- Since this is the main project (the one we build), it must reference all other subprojects to ensure they're also built.
这是专用工作器的配置(您使用
new Worker()
创建的那种).This is the config for the dedicated worker (the kind you create with
new Worker()
).{ "extends": "../generic-tsconfig.json", "compilerOptions": { "lib": ["esnext", "webworker"], } }
除非您从中导入内容(例如类型),否则您无需在此处引用其他子项目.
You don't need to reference the other sub projects here unless you import things from them (eg types).
TypeScript 不会区分不同的 worker 上下文,尽管它们具有不同的全局变量.因此,事情变得有点混乱:
TypeScript doesn't differentiate between different worker contexts, despite them having different globals. As such, things get a little messy:
postMessage('foo');
这是有效的,因为 TypeScript 的webworker"类型为所有专用工作器全局变量创建全局变量.但是:
This works, as TypeScript's "webworker" types create globals for all dedicated worker globals. However:
self.postMessage('foo');
...这失败了,因为 TypeScript 给了
self
一个不存在的类型,它有点像一个抽象的全局工人.…this fails, as TypeScript gives
self
a non-existent type that's sort-of an abstract worker global.要解决此问题,请将其包含在您的来源中:
To fix this, include this in your source:
declare var self: DedicatedWorkerGlobalScope; export {};
这会将
self
设置为正确的类型.This sets
self
to the correct type.除非文件是模块,否则
declare var
位不起作用,并且虚拟导出使 TypeScript 将其视为模块.这意味着您在模块范围内声明了self
,该范围目前不存在.否则,您将尝试在全局声明它确实已经存在.The
declare var
bit doesn't work unless the file is a module, and the dummy export makes TypeScript treat it as a module. This means you're declaringself
in the module scope, which doesn't currently exist. Otherwise, you're trying to declare it on the global, where it does already exist.同上.
{ "extends": "../generic-tsconfig.json", "compilerOptions": { "lib": ["esnext", "webworker"], } }
使用 Service Worker 类型
如上所述,TypeScript 的webworker"类型为所有专用工作器全局变量创建全局变量.但这不是一个专门的工人,所以有些类型是不正确的:
Using service worker types
As above, TypeScript's "webworker" types create globals for all dedicated worker globals. But this isn't a dedicated worker, so some of the types are incorrect:
postMessage('yo');
TypeScript 不会抱怨上述问题,但它会在运行时失败,因为
postMessage
不在 service worker 全局上.TypeScript doesn't complain about the above, but it'll fail at runtime as
postMessage
isn't on the service worker global.不幸的是,您无法修复真正的全局,但您仍然可以修复
self
:Unfortunately there's nothing you can do to fix the real global, but you can still fix
self
:declare var self: ServiceWorkerGlobalScope; export {};
现在您需要确保通过
self
访问每个对 Service Worker 特殊的全局变量.Now you need to ensure every global that's special to service workers is accessed via
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); });
其他工作器类型(例如工作集和共享工作器)也存在相同的问题和解决方法.
The same issues and workarounds exist for other worker types, such as worklets and shared workers.
tsc --build src/main
就是这样!
这篇关于与工人一起构建 TypeScript 项目的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!