简体   繁体   English

Typescript generics 类型推断

[英]Typescript generics type inferring

I am playing with typescript by implementing a strongly typed rest request mechanism.我通过实现强类型 rest 请求机制来玩 typescript。

Let the code speak:让代码说话:

With this type I want to define the mapping between the routes and the related type of objects:使用这种类型,我想定义路由和相关对象类型之间的映射:

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

export type ApiScope = keyof RoutesMapping

The following function is the one I am willing to use to make POST requests下面的 function 是我愿意用来发出 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
  });
}

I expect to call this function in the following way我希望通过以下方式调用这个 function

const data = await restPost("users")

An make typescript infer the return type by inferring it by the scope and the route.一个 make typescript 通过 scope 和路由推断返回类型。

Actually, using it with the default type parameters, it works:实际上,将它与默认类型参数一起使用,它可以工作:

在此处输入图像描述

The problem is when I when I want to call the other api in this way:问题是当我想以这种方式调用另一个 api 时:

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

Unfortunately, it does not work and it infers all the possible types不幸的是,它不起作用,它推断出所有可能的类型

在此处输入图像描述

The only way to solve the problem is to explicitly add the second type parameter解决问题的唯一方法是显式添加第二个类型参数

在此处输入图像描述

How can I use all of this without needing to add the second type parameter in the second scenario?如何在不需要在第二种情况下添加第二个类型参数的情况下使用所有这些?

Here is a typescript playground这是一个typescript 操场

If you infer the api-key type parameter, you can actually construct a solution that does what you want:如果您推断出 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>>> {
 ..
}

When applied to a route string, the correct return type for restPost is inferred, without needing to specify any type parameters:当应用于路由字符串时,会推断出restPost的正确返回类型,而无需指定任何类型参数:

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

TypeScript playground TypeScript操场

Note that this assumes the route keys are unique, which seems to be the case since the api key is not passed to restPost .请注意,这假设路由键是唯一的,这似乎是因为 api 键未传递给restPost I'm also not certain it is wise to introduce all this complexity, but at least it is possible.我也不确定引入所有这些复杂性是否明智,但至少这是可能的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM