【问题标题】:Structuring a TypeScript project with workers用 worker 构建一个 TypeScript 项目
【发布时间】:2019-10-14 20:02:28
【问题描述】:

我应该如何构建一个包含主线程 (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 作斗争:

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

此外,我如何在工作人员中声明全局?以前我用过declare var self: DedicatedWorkerGlobalScope,但是有没有办法真正设置全局(而不是仅仅设置self)?

【问题讨论】:

    标签: typescript service-worker web-worker


    【解决方案1】:

    非常感谢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”类型会为所有专用的 worker 全局变量创建全局变量。然而:

    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”类型为所有专用工作全局变量创建全局变量。但这不是一个专门的worker,所以有些类型是不正确的:

    postMessage('yo');
    

    TypeScript 不会抱怨上述问题,但它会在运行时失败,因为 postMessage 不在 service worker 全局中。

    不幸的是,您无法修复真正的全局,但您仍然可以修复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
    

    就是这样!

    【讨论】:

    • 感谢@JaffaTheCake 的详细解释!收藏这个。
    • 很好的答案 +1。仅供参考,我已经创建了一个带有 Web Worker 的 TS 的工作示例的最小设置,这里是我的解决方案:github.com/gibbok/typescript-web-workers 我使用对 DedicatedWorkerGlobalScope 的强制转换来帮助 TS 获取自己的类型。我希望能有用。
    • @GibboK 您链接的那个项目并不能解决 worker 和 dom 类型之间的冲突问题,因为它完全省略了 dom 类型,我认为您的示例可能太小,无法在现实世界中提供帮助.
    猜你喜欢
    • 2020-08-29
    • 1970-01-01
    • 1970-01-01
    • 2018-03-07
    • 2019-06-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多