TypeScript区分了与泛型的联合 [英] TypeScript discriminated union with generics

查看:86
本文介绍了TypeScript区分了与泛型的联合的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

Is there a way to set up a discriminated union that lets you capture a specific type with each union member? I'm trying to write a type safe command handler along the lines of:

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中实现此目标的好模式?

The idea is that the CommandExecutor would not only knows the fields in each command after narrowing, but could also verify that the return type is as required for each command. Is there a good pattern to achieve this in TypeScript?

推荐答案

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

You can create a relation between the command type and the result type by using an overload that captures the passed in command type and a mapping interface (you could also use a result union, but extracting the result type would not cause errors for missing command types, so I favor the mapping interface in this case).

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

We can't ensure the return type in the implementation corresponds to the expected return type directly. The best we can do is use an extra function to validate this in the switch :


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))
    }
  }
}

游乐场

这篇关于TypeScript区分了与泛型的联合的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆