我的最终目标是像这样使用 useModel 钩子:
useModel(Art, "get", { id: 7 })
useModel(Art, "post", { data: { title: "Art title" } })
但是我在正确输入该钩子的第三个参数时遇到问题,因为它依赖于方法(第二个参数)。所有模型都具有相同的方法和相同的参数,并且从保存这些方法的同一个类进行扩展。方法参数可能不同,如下面的代码示例所示。
class BaseApiModel {
protected static endpoint = "";
public static key = "";
...
static async get<T>(id?: number, params?: QueryFilterParams) {
const axios = this.getAxiosInstance();
const response: ResourceResponse<T> = await axios({
method: "GET",
url: id ? `${this.endpoint}/${id}` : this.endpoint,
params,
});
// TODO: type the response (ResourceResponse<T> | CollectionResponse<T> | PaginatedResponse<T> | CountResponse)
return response;
}
static async post<T>(data: Omit<T, "id">) {
const axios = this.getAxiosInstance();
const response: ResourceResponse<T> = await axios({
method: "POST",
url: this.endpoint,
data,
});
return response;
}
...
}
这是将传递给钩子的示例模型:
class Art extends BaseApiModel {
protected static endpoint = "/art";
public static key = "art";
id!: number;
title!: string;
description!: string;
image?: File;
}
这是钩子:
type Methods = "get" | "post" | "put" | "patch" | "delete";
function useModel<T extends typeof BaseApiModel>(
model: T,
method: Methods,
options?: Record<string, any>
) {
const query = useQuery({
queryKey: [model.key, method, options],
queryFn: model[method](options),
enabled: method === "get",
});
const mutation = useMutation({
mutationKey: [model.key, method, options],
mutationFn: model[method](options),
});
if (method === "get") {
return query;
}
return mutation;
}
您可以对第三个参数使用条件类型:
type Methods = "get" | "post" | "put" | "patch" | "delete";
type QueryFilterParams = Record<string, string>;
function useModel<T extends typeof BaseApiModel, M extends Methods>(
model: T,
method: M,
options?: M extends 'get' ? {id?: number, params?: QueryFilterParams } : M extends 'post' ? {data: Omit<InstanceType<T>, "id"> } : never
) {
}
type ResourceResponse<T> = T;
class BaseApiModel{
protected static endpoint = "";
public static key = "";
static async get<T>(id?: number, params?: QueryFilterParams) {
const response: ResourceResponse<T> = await axios({
method: "GET",
url: id ? `${this.endpoint}/${id}` : this.endpoint,
params,
});
// TODO: type the response (ResourceResponse<T> | CollectionResponse<T> | PaginatedResponse<T> | CountResponse)
return response;
}
static async post<T>(data: Omit<T, "id">) {
const response: ResourceResponse<T> = await axios({
method: "POST",
url: this.endpoint,
data,
});
return response;
}
}
class Art extends BaseApiModel{
title = 'Art title';
}
useModel(Art, "get", { id: 7 })
useModel(Art, "post", { data: { title: "Art title" } })