首页 > 解决方案 > 强类型工作者

问题描述

我正在玩一些逻辑来创建一个强类型的工作者,它应该能够只接受一组预定义的消息,并在给定消息的情况下以适当的响应类型进行响应。

类似于以下内容

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

tsc正如抱怨的那样,这不起作用

'FooMsg' 类型的参数不能分配给'never' 类型的参数。交集 '{ type: "foo"; 有效载荷:数字;} & { 类型:“酒吧”;有效载荷:布尔值;} & { 类型:“baz”;有效载荷:字符串;}' 被简化为 'never' 因为属性 'type' 在某些成分中具有冲突类型.ts(2345)

有一个更好的方法吗?或者更确切地说,一个有效的?

标签: typescript

解决方案


你快到了。

问题在于createWorker返回类型,它是函数的联合。

当您想调用函数联合时,它们的参数将相交/合并。因为函数参数处于逆变位置。这就是为什么你有never类型。

你可以在我的文章中阅读更多关于它的信息

您需要做的就是产生函数重载,换句话说,您应该派生函数的交集而不是联合:

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

// credits goes to https://stackoverflow.com/a/50375286
type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (
    k: infer I
) => void
    ? I
    : never;

declare const createWorker: <T extends MsgStruct<unknown, unknown,unknown>>(worker: Worker) => UnionToIntersection<StronglyTypedWorker<T>>

// Example
const strongWorker = createWorker<WorkerMsg>(worker)

declare const foo: FooMsg

const fooRes = strongWorker.postMessage(foo)
    .then(res => { }) // res is number

操场

顺便说一句,大多数时候你不需要函数的联合


推荐阅读