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

时间:2019-05-29 08:54:18

标签: typescript service-worker web-worker

我应该如何构建一个包含主线程(DOM)脚本和工作程序的项目?例如:

main.ts

// 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对抗:

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

此外,如何在worker中声明全局变量?以前我使用过declare var self: DedicatedWorkerGlobalScope,但是有没有一种方法可以实际设置全局变量(而不仅仅是设置self)?

1 个答案:

答案 0 :(得分:20)

非常感谢Mattias Buelens向我指出了正确的方向。

Here's a working example

项目结构为:

  • 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目录,每个子项目(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"}
  ]
}
  • 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

就是这样!