我可以从打字稿中的泛型类型引用参数的类型吗?

时间:2019-12-22 23:25:15

标签: typescript generics

我试图将类型检查添加到通过Web套接字创建的api的前端。我省去了我认为与该问题无关的复杂部分,但是如果需要,我可以提供更多信息。我有一台服务器在Web套接字上侦听格式为c|endpoint|command|data作为字符串的消息,并将使用请求的数据进行答复。该部分工作良好,但我想在客户端部分添加类型检查,以帮助调试。每个端点都有多个命令,每个端点都带有自己的自定义数据对象,该对象在发送之前已进行了字符串化,因此每个命令必须是一个函数,该函数接收套接字和数据并返回发送回的数据。

interface Socket {
  send(data: string): void
}
interface Command<T> {
  (socket: Socket, data: T): string | Promise<string>
}
interface Endpoints {
  [index: string]: {
    [index: string]: Command<any>
  },
  test: {
    hello: Command<{testarg: string}>
  }
}

此示例具有一个端点(test)和一个命令(hello),该命令采用结构为{testarg: string}的数据。其他命令将具有不同的所需数据结构。

现在,我还有另一个功能可以从客户端发送命令,这是我希望进行类型检查的地方。

class ClientSocket {
  // some parts not shown
  socket!: WebSocket

  call<T extends keyof Endpoints, K extends keyof Endpoints[T], U>(endpoint: T, command: K, data: U) {
    // call the api: this.socket.send(`c|${endpoint}|${command}|${JSON.stringify(data)}`) and save the promise resolve for later resolution when we get a reply. I left that out since it's not exactly relevant.
    return "promise for results of api call"
  }
}

我想调用上面的三个参数,端点,命令和数据,将它们类型检查到Endpoints接口,所以如果尝试使用它,则会出现错误:call("test", "hello", {}),因为它缺少{ {1}}属性。我想出了如何检查前两个命令,所以我调用了一个有效的命令,但是我不知道如何对数据(U)进行类型检查。

感谢您的帮助。

1 个答案:

答案 0 :(得分:2)

您需要编写一个小的类型包装器,以获取Endpoints [T] [K]中命令的数据类型:

type CommandData<T extends keyof Endpoints, K extends keyof Endpoints[T]> = Endpoints[T][K] extends Command<infer TData> ? TData : never;
...
call<T extends keyof Endpoints, K extends keyof Endpoints[T]>(endpoint: T, command: K, data: CommandData<T, K>) {
        // call the api: this.socket.send(`c|${endpoint}|${command}|${JSON.stringify(data)}`) and save the promise resolve for later resolution when we get a reply. I left that out since it's not exactly relevant.
        return "promise for results of api call"
    }
...
new ClientSocket().call("test", "hello", { testarg: "hello" }); // { testarg: string } is expected

但是您可以将其缩短为:

...
call<T extends keyof Endpoints, K extends keyof Endpoints[T]>(endpoint: T, command: K, data: Endpoints[T][K] extends Command<infer TData> ? TData : never) {
...

有关infer关键字的更多信息,请参见https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-8.html