【问题标题】:Typescript generics type inferringTypescript 泛型类型推断
【发布时间】:2021-03-03 02:48:38
【问题描述】:

我通过实现一个强类型的休息请求机制来玩打字稿。

让代码说话:

使用这种类型,我想定义路由和相关对象类型之间的映射:

export interface RoutesMapping {
    api1: {
        users: UserApiModel
        products: ProductApiModel,
    }
    api2: {
        "other-route": OtherModel1,
        "another-one-route": OtherModel2
    }
}

export type ApiScope = keyof RoutesMapping

以下函数是我愿意用来发出 POST 请求的函数

export type RestApiPostResponse<T = any> = {
    result: boolean
    data: T
}

export function restPost<S extends keyof RoutesMapping = "api1", T extends keyof RoutesMapping[S] = keyof RoutesMapping[S]>(
  route: T,
  // nervermind this object, is out of scope for the question
  options: ApiRequestOptions<S, any> = {}
): Promise<RestApiPostResponse<RoutesMapping[S][T]>> {
  const url = apiUtils.buildUrl(route as string, options)
  const { payload, headers, isProtected } = options
  return post({
    url,
    isProtected,
    payload,
    headers
  });
}

我希望通过以下方式调用这个函数

const data = await restPost("users")

make typescript 通过作用域和路由推断返回类型。

实际上,使用默认类型参数,它可以工作:

问题是当我想以这种方式调用另一个api时:

const data = await restPost<"api2">("other-route")

不幸的是,它不起作用,它推断出所有可能的类型

解决问题的唯一方法是显式添加第二个类型参数

如何在第二个场景中不用添加第二个类型参数的情况下使用所有这些?

这是typescript playground

【问题讨论】:

  • 请考虑修改此处的代码,以便构成一个适合放入独立 IDE 的 minimal reproducible example,例如 The TypeScript Playground,这表明您的问题并且没有其他问题(例如,未声明的类型或值)存在。相关,images of code or errors are not as helpful as you might think;对我来说,如果我不能将它复制并粘贴到我的 IDE 中,我就不能轻松地用它来诊断或提出问题的解决方案。祝你好运!
  • 我最后添加了指向操场@jcalz的链接
  • 您的游乐场链接无效
  • @ritaj 我用稍微改变的版本更新了链接:操场不是用来运行的,问题是 100% 的类型检查,在最后的代码行中有一条注释解释了什么我正在努力实现
  • @Apperside 谢谢...查看this question 及其答案,看看它是否解决了您的问题。 TS 中没有部分类型参数推断;我知道的解决方法是柯里化和虚拟参数,请参阅here。

标签: typescript typescript-typings typescript-generics


【解决方案1】:

如果你推断出 api-key 类型参数,你实际上可以构造一个做你想做的解决方案:

type Model<Route> = // Returns the model value for key Route in RoutesMapping
  keyof RoutesMapping extends infer Api
  ? Api extends keyof RoutesMapping 
    ? Route extends keyof RoutesMapping[Api]
      ? RoutesMapping[Api][Route]
      : never
    : never
  : never

type Routes<Api> = Api extends {} ? keyof Api  : never // Helper to distribute keyof over a union of objects
type AllRoutes = Routes<RoutesMapping[keyof RoutesMapping]> // Union of all route keys: 'users' | 'products' | 'other-route' | 'another-one-route'

export function restPost<Route extends AllRoutes>(
  route: Route,
  options?:{url:string,payload:any}
): Promise<RestApiPostResponse<Model<Route>>> {
 ..
}

当应用于路由字符串时,会推断出restPost 的正确返回类型,而无需指定任何类型参数:

const data = await restPost("users") // data: RestApiPostResponse<UserApiModel>
const data2 = await restPost("other-route") // data2: RestApiPostResponse<OtherModel1>

TypeScript playground

请注意,这假设路由键是唯一的,这似乎是因为 api 键未传递给restPost。我也不确定引入所有这些复杂性是否明智,但至少是可能的。

【讨论】:

    猜你喜欢
    • 2019-09-17
    • 2017-10-04
    • 2016-12-05
    • 2018-01-18
    • 2021-07-28
    • 1970-01-01
    • 2020-07-23
    • 2021-10-18
    • 2020-09-09
    相关资源
    最近更新 更多