我正在通过实现强类型的休息请求机制来玩打字稿。
让代码说话:
使用这种类型,我想定义路由和相关对象类型之间的映射:
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 打字稿通过范围和路由推断返回类型。
实际上,将它与默认类型参数一起使用是可行的:
问题是当我想以这种方式调用其他api时:
const data = await restPost<"api2">("other-route")
不幸的是,它不起作用,它推断出所有可能的类型
解决问题的唯一方法是显式添加第二个类型参数
如何使用所有这些而不需要在第二个场景中添加第二个类型参数?
答案 0 :(得分: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>
请注意,这里假设路由键是唯一的,这似乎是因为 api 键没有传递给 restPost
。我也不确定引入所有这些复杂性是否明智,但至少是可能的。