Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using WebWorkers in Typescript with Webpack and worker-loader without custom loader strings

I'm trying to get web workers playing nice with Typescript and Webpack's worker-loader. There's an example in their documentation of achieving this through a custom module declaration, however this relies on using the webpack worker-loader!./myWorker syntax.

I want to be able to load workers through a custom *.worker.js webpack use rule rather than explicitly listing the loader in the import string. Something about typescript doesn't seem to like defining or modifying a modules declaration if it's a plain relative import, and gets stuck on the fact that the worker file is not a module, even with a module declaration for it.

I have a worker that looks like

// test.worker.ts
/// <reference lib="webworker" />

const worker: DedicatedWorkerGlobalScope = self as any;

worker.onmessage = ({ data }) => {
  if (data instanceof Array) {
    worker.postMessage(data.join(' ') + '!');
  }
};

A declaration that looks like

// custom.d.ts
declare module '*.worker' {
  class TestWorker extends Worker {
    constructor();
  }

  export default TestWorker
}

And is used in my main app as

import TestWorker from './test.worker';

const testWorker = new TestWorker();
testWorker.onmessage = ({ data }: { data: string }) => {
  console.log(data);
};

testWorker.postMessage([
  'hello',
  'I',
  'am',
  'a',
  'web',
  'worker',
]);

The error output is

TypeScript error in /worker-test/src/index.tsx(9,24):File '/worker-test/src/test.worker.ts' is not a module.  TS2306

Changing the import to worker-loader!./test.worker seems to work and gets typescript to understand the custom declaration, however I'm really trying to avoid using custom loader strings as the intention is to integrate this into create-react-app which doesn't allow these.

Is there a way to get regular relative imports to recognise a custom module declaration?

like image 575
Matt D Avatar asked Apr 03 '19 05:04

Matt D


1 Answers

You were almost there!

Change the module declaration to include the file extension:

// custom.d.ts
declare module '*.worker.ts' {
  class TestWorker extends Worker {
    constructor();
  }

  export default TestWorker
}

And your import to also include the file extension:

import TestWorker from './test.worker.ts';

This did it for me - specifying the extension in the import stops TS from importing it as a module, and specifying the extension in the declaration stops TS from complaining since you're importing a .ts file by also specifying the extension.

like image 92
Fabio Lolli Avatar answered Oct 23 '22 16:10

Fabio Lolli