TypeScript区分了与泛型的联合

时间:2019-07-30 07:21:31

标签: typescript generics discriminated-union

有没有一种方法可以建立一个可区分的联合,让您与每个联合成员一起捕获特定类型?我试图按照以下方式编写类型安全的命令处理程序:

interface GetUsersCommand { // returns User[]
  type: 'GET_USERS'
}

interface UpdateUserNameCommand { // returns void
  type: 'UPDATE_USER_NAME'
  userId: string
  name: string
}

type Command<Result> = GetUsersCommand | UpdateUserNameCommand

class CommandExecutor {
  execute<Result>(command: Command<Result>): Result {
    switch (command.type) {
      case 'GET_USERS': return [ user1, user2, user3 ]
      case 'UPDATE_USER_NAME': updateUserName(command.userId, command.name)
    }
  }
}

这个想法是CommandExecutor不仅会在缩小范围后知道每个命令中的字段,而且还可以验证返回类型是否符合每个命令的要求。有没有在TypeScript中实现此目标的好模式?

1 个答案:

答案 0 :(得分:2)

您可以通过使用重载来创建命令类型与结果类型之间的关系,该重载捕获传入的命令类型和映射接口(也可以使用结果并集,但是提取结果类型不会导致以下错误:缺少命令类型,因此在这种情况下,我倾向于使用映射接口。

我们不能确保实现中的返回类型直接对应于预期的返回类型。我们能做的最好的就是在开关中使用一个额外的功能来验证这一点:


interface GetUsersCommand { // returns User[]
  type: 'GET_USERS'
}

interface CommandResults {
  'GET_USERS': Users[]
}

interface UpdateUserNameCommand { // returns void
  type: 'UPDATE_USER_NAME'
  userId: string
  name: string
}
interface CommandResults {
  'UPDATE_USER_NAME': void
}
type Command = GetUsersCommand | UpdateUserNameCommand

function checkResult<T extends  keyof CommandResults>(type: T, result: CommandResults[T])  {
  return result;
}

class CommandExecutor {
  execute<T extends Command>(command: T): CommandResults[T['type']]
  execute(command: Command): CommandResults[keyof CommandResults] {
    switch (command.type) {
      case 'GET_USERS': return checkResult(command.type, [ user1, user2, user3 ])
      case 'UPDATE_USER_NAME': return checkResult(command.type, updateUserName(command.userId, command.name))
    }
  }
}

Playground