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

问题描述 投票:0回答:2

我应该如何构建一个包含主线程 (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
)?

typescript service-worker web-worker
2个回答
93
投票

非常感谢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"],
  }
}

使用 Service Worker 类型

如上所述,TypeScript 的“webworker”类型为所有专用工作全局变量创建全局变量。但这不是一个专门的工人,所以有些类型是不正确的:

postMessage('yo');

TypeScript 不会抱怨上述情况,但它会在运行时失败,因为

postMessage
不在服务工作线程全局中。

不幸的是,你无法修复真正的全局,但你仍然可以修复

self

declare var self: ServiceWorkerGlobalScope;
export {};

现在您需要确保通过

self
访问每个对 Service Worker 特殊的全局变量。

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

就是这样!


0
投票

我正在使用 Vite + TS 的标准设置,我不想摆弄多个 tsconfig,因为我唯一需要的是对

webworkers
库的引用(其余部分相同)。

这可以通过在worker.js中使用tripleslash指令来实现:

/// <reference lib="webworker" />

// ...
// rest of the worker code
// ...
postMessage("hi", [transferable]);
© www.soinside.com 2019 - 2024. All rights reserved.