首页 > 解决方案 > 我可以从打字稿中的泛型类型中引用参数的类型吗?

问题描述

我正在尝试将类型检查添加到我通过 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", {})因为它缺少testarg属性。我想出了如何检查前两个,所以我调用了一个有效的命令,但我不知道如何对数据 (U) 进行类型检查。

谢谢您的帮助。

标签: typescriptgenerics

解决方案


您需要编写一个小型类型包装器,以获取 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


推荐阅读