I'm playing around with some logic to create a strongly typed worker, which should be able to accept only a pre-defined set of messages, and respond with the appropriate response type given the message.
Something like the following
type MsgStruct<T, P, R> = {
type: T
payload: P
response: R
}
type FooMsg = MsgStruct<'foo', number, number>
type BarMsg = MsgStruct<'bar', boolean, string>
type BazMsg = MsgStruct<'baz', string, boolean[]>
// Messages that can be handled by the worker
type WorkerMsg =
| FooMsg
| BarMsg
| BazMsg
// Creation logic
type StronglyTypedWorker<T> = T extends { type: infer A, payload: infer B, response: infer C } ? {
postMessage: (msg: { type: A, payload: B }) => Promise<C>
} : never
declare const worker: Worker
declare const createWorker: <T extends MsgStruct<unknown, unknown, unknown>>(worker: Worker) => StronglyTypedWorker<T>
// Example
const strongWorker = createWorker<WorkerMsg>(worker)
declare const foo: FooMsg
const fooRes = strongWorker.postMessage(foo)
.then(res => {}) // expecting `res` to be of type `number` here
This doesn't work, as tsc
complains with
Argument of type 'FooMsg' is not assignable to parameter of type 'never'. The intersection '{ type: "foo"; payload: number; } & { type: "bar"; payload: boolean; } & { type: "baz"; payload: string; }' was reduced to 'never' because property 'type' has conflicting types in some constituents.ts(2345)
Is there a better way to do this? Or rather, one that works?