8

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?

1 Answer 1

9

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.

Sign up to request clarification or add additional context in comments.

1 Comment

It's unfortunate that this is necessary as basically every other can leave the .ts off, which is especially useful in a mixed js/ts/tsx environment. But given the special nature of workers and this override, this is by far the best tradeoff. Thanks for taking the time to answer!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.