与工作人员一起构造TypeScript项目

Jaf*_*ake 29 web-worker typescript service-worker

我应该如何构建一个包含主线程(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);
};
Run Code Online (Sandbox Code Playgroud)

工人

// 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);
Run Code Online (Sandbox Code Playgroud)

每当我过去尝试过此操作时,我都会感觉自己一直在与TypeScript对抗:

  • 使用tsconfig.json同时具有worker和DOM类型的对象。但这当然不是准确的类型。
  • 使用多个tsconfig.json。但是项目边界使它们之间的类型引用变得困难。

另外,如何在工作程序中声明全局变量?以前我曾经使用过declare var self: DedicatedWorkerGlobalScope,但是有没有一种方法可以实际设置全局(而不只是设置self)?

Jaf*_*ake 28

非常感谢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
  }
}
Run Code Online (Sandbox Code Playgroud)

我故意避免调用this tsconfig.json,因为它本身不是项目。使以上内容适应您的需求。以下是重要部分:

  • outDir -这是转译的脚本,声明和源映射所在的位置。
  • rootDir-通过此设置为src目录,每个子项目(maindedicated-workerservice-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"}
  ]
}
Run Code Online (Sandbox Code Playgroud)
  • extends -这指向我们上面的通用配置。
  • compilerOptions.lib-该项目使用的库。在这种情况下,使用JS和DOM。
  • references -由于这是主要项目(我们构建的项目),因此它必须引用所有其他子项目以确保它们也已构建。

src/dedicated-worker/tsconfig.json

这是专用工作程序(您使用创建的工作程序new Worker())的配置。

{
  "extends": "../generic-tsconfig.json",
  "compilerOptions": {
    "lib": ["esnext", "webworker"],
  }
}
Run Code Online (Sandbox Code Playgroud)

除非您从其他子项目中导入内容(例如类型),否则您无需在此处引用其他子项目。

使用专用的工作者类型

尽管TypeScript具有不同的全局变量,但它们不会在不同的工作程序上下文之间进行区分。因此,事情变得有些混乱:

postMessage('foo');
Run Code Online (Sandbox Code Playgroud)

这是可行的,因为TypeScript的“ webworker”类型会为所有专用的工作人员全局变量创建全局变量。然而:

self.postMessage('foo');
Run Code Online (Sandbox Code Playgroud)

…这失败了,因为TypeScript给出self了不存在的,类似于全局抽象工作程序的类型。

要解决此问题,请将其包括在您的源代码中:

declare var self: DedicatedWorkerGlobalScope;
export {};
Run Code Online (Sandbox Code Playgroud)

设置self为正确的类型。

declare var除非文件是模块,否则该位不起作用,并且伪导出使TypeScript将其视为模块。这意味着您要self在模块范围中声明,该范围当前不存在。否则,你要声明它在全球,它确实存在。

src/service-worker/tsconfig.json

同上。

{
  "extends": "../generic-tsconfig.json",
  "compilerOptions": {
    "lib": ["esnext", "webworker"],
  }
}
Run Code Online (Sandbox Code Playgroud)

使用服务人员类型

如上所述,TypeScript的“ webworker”类型为所有专用工作程序全局变量创建全局变量。但这不是专门的工作人员,因此某些类型不正确:

postMessage('yo');
Run Code Online (Sandbox Code Playgroud)

TypeScript不会抱怨以上内容,但是它将在运行时失败,postMessage而不是在服务工作程序全局上出现。

不幸的是,您无法采取任何措施来修复真正的全局变量,但是您仍然可以修复self

declare var self: ServiceWorkerGlobalScope;
export {};
Run Code Online (Sandbox Code Playgroud)

现在,您需要确保通过可以访问服务人员专用的每个全局变量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);
});
Run Code Online (Sandbox Code Playgroud)

对于其他类型的工作程序(例如工作集和共享工作程序),存在相同的问题和解决方法。

建造

{
  "compilerOptions": {
    "target": "esnext",
    "module": "esnext",
    "strict": true,
    "moduleResolution": "node",
    "rootDir": ".",
    "outDir": "../dist",
    "composite": true,
    "declarationMap": true,
    "sourceMap": true
  }
}
Run Code Online (Sandbox Code Playgroud)

就是这样!

  • @GibboK,您链接的项目不能解决worker和dom类型之间的冲突问题,因为它完全省略了dom类型,我认为您的示例可能太小,在现实世界中没有帮助。 (3认同)