Build Mode for TypeScript Running tsc --build ( tsc -b for short) will do the following: Find all referenced projects. Detect if they are up-to-date. Build out-of-date projects in the correct order.
Many thanks to Mattias Buelens who pointed me in the right direction.
Here's a working example.
The project structure is:
dist
src
generic-tsconfig.json
main
tsconfig.json
dedicated-worker
tsconfig.json
service-worker
tsconfig.json
src/generic-tsconfig.json
This contains the config common to each project:
{
"compilerOptions": {
"target": "esnext",
"module": "esnext",
"strict": true,
"moduleResolution": "node",
"rootDir": ".",
"outDir": "../dist",
"composite": true,
"declarationMap": true,
"sourceMap": true
}
}
I've deliberately avoided calling this tsconfig.json
, as it isn't a project itself. Adapt the above to your needs. Here are the important parts:
outDir
- This is where the transpiled script, declarations and source maps will go.rootDir
- By setting this to the src
directory, each of the subprojects (main
, dedicated-worker
, service-worker
) will appear as subdirectories in the outDir
, otherwise they'll try and share the same directory and overwrite each other.composite
- This is required for TypeScript to keep references between projects.Do not include references
in this file. They'll be ignored for some undocumented reason (this is where I got stuck).
src/main/tsconfig.json
This is the config for the 'main thread' project, As in, the JavaScript that will have access to the document.
{
"extends": "../generic-tsconfig.json",
"compilerOptions": {
"lib": ["esnext", "dom"],
},
"references": [
{"path": "../dedicated-worker"},
{"path": "../service-worker"}
]
}
extends
- This points to our generic config above.compilerOptions.lib
- The libs used by this project. In this case, JS and the DOM.references
- Since this is the main project (the one we build), it must reference all other subprojects to ensure they're also built.src/dedicated-worker/tsconfig.json
This is the config for the dedicated worker (the kind you create with new Worker()
).
{
"extends": "../generic-tsconfig.json",
"compilerOptions": {
"lib": ["esnext", "webworker"],
}
}
You don't need to reference the other sub projects here unless you import things from them (eg types).
TypeScript doesn't differentiate between different worker contexts, despite them having different globals. As such, things get a little messy:
postMessage('foo');
This works, as TypeScript's "webworker" types create globals for all dedicated worker globals. However:
self.postMessage('foo');
…this fails, as TypeScript gives self
a non-existent type that's sort-of an abstract worker global.
To fix this, include this in your source:
declare var self: DedicatedWorkerGlobalScope;
export {};
This sets self
to the correct type.
The declare var
bit doesn't work unless the file is a module, and the dummy export makes TypeScript treat it as a module. This means you're declaring self
in the module scope, which doesn't currently exist. Otherwise, you're trying to declare it on the global, where it does already exist.
src/service-worker/tsconfig.json
Same as above.
{
"extends": "../generic-tsconfig.json",
"compilerOptions": {
"lib": ["esnext", "webworker"],
}
}
As above, TypeScript's "webworker" types create globals for all dedicated worker globals. But this isn't a dedicated worker, so some of the types are incorrect:
postMessage('yo');
TypeScript doesn't complain about the above, but it'll fail at runtime as postMessage
isn't on the service worker global.
Unfortunately there's nothing you can do to fix the real global, but you can still fix self
:
declare var self: ServiceWorkerGlobalScope;
export {};
Now you need to ensure every global that's special to service workers is accessed via 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);
});
The same issues and workarounds exist for other worker types, such as worklets and shared workers.
tsc --build src/main
And that's it!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With