【问题标题】:Strongly typed worker强类型工作者
【发布时间】:2021-06-28 08:18:46
【问题描述】:

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

类似下面的东西

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


    【解决方案1】:

    你快到了。

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

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

    您可以在我的article 中阅读更多相关信息

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

    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
    

    Playground

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-02-15
      • 1970-01-01
      • 2020-09-02
      • 2023-03-30
      • 1970-01-01
      • 1970-01-01
      • 2012-09-04
      • 1970-01-01
      相关资源
      最近更新 更多