如何实现使用 TypeMap 返回的函数

Pet*_*ert 5 typescript

我有一个歧视性工会类型Actions。对于每个操作,我有一个不同的处理函数,返回不同的结果。我现在需要一个通用处理程序函数,该函数基于action.type调用相应的处理程序函数并返回其结果。该函数的类型签名应该使得 TypeScript 可以根据调用的参数推断结果的形状。我通过类型签名来实现这一点const handler = <A extends Action>(a: A): HandlerMap[A['type']] => ...。然而,TypeScript 在实现的 switch 语句中抱怨返回无效:

Type 'Result1' is not assignable to type 'HandlerMap[A["type"]]'.
  Type 'Result1' is not assignable to type 'Result1 & Result2'.
    Type 'Result1' is not assignable to type 'Result2'.
Run Code Online (Sandbox Code Playgroud)

我错过了什么/做错了什么?

这是完整的代码:

type Action1 = { type: 'A1'; input: { i: string } }
type Result1 = { result: string }
const handler1 = (a: Action1): Result1 => ({ result: a.input.i + '!' })

type Action2 = { type: 'A2'; input: { x: number } }
type Result2 = { result: { r2: number } }
const handler2 = (a: Action2): Result2 => ({ result: { r2: a.input.x + 1 } })

type Action = Action1 | Action2

type HandlerMap = {
  A1: Result1
  A2: Result2
}

const handler = <A extends Action>(a: A): HandlerMap[A['type']] => {
  switch (a.type) {
    case 'A1':  return handler1(a) // <--Compiler complains here
    case 'A2':  return handler2(a)
    default:    return undefined
  }
}
Run Code Online (Sandbox Code Playgroud)

Tob*_* S. 3

我会添加这种方法:

const handler = <A extends Action>(a: A): HandlerMap[A['type']] => {

  const map: { 
    [K in keyof HandlerMap]: (a: Extract<Action, { type: K }>) => HandlerMap[K] 
  } = {
    "A1": (a) => handler1(a),
    "A2": (a) => handler2(a),
  }

  return map[a.type](a as any) as HandlerMap[A["type"]]
}
Run Code Online (Sandbox Code Playgroud)

switch我们可以用一个名为 的对象替换mapK该对象包含每个键type和回调。此回调的返回类型严格键入为HandlerMap[K]

优点:

  • map是详尽的,如果你忘记处理一个案例,将会给出一个编译时错误
  • 每个回调中的参数a都被严格指定为正确的Action类型。所以a.input.i在第一个回调中可以做类似的事情
  • 回调的返回类型是严格类型化的。因此handler1(a)在第二个回调中返回将导致编译时错误

缺点:

  • 我们必须as any在 return 语句中使用(感谢相关的 union 问题

  • as HandlerMap[A["type"]]由于 TypeScript 急于解析 tomap[a.type]()Result1 | Result2不是 ,我们还必须使用{ "A1": ..., "A2": ... }[A["type"]](A)


操场