如何在RTK查询中以类型安全的方式注入端点



我正在尝试分解我的一些RTK查询代码。我的大多数端点都是实体的简单CRUD端点,所以我的许多.injectEndpoints()调用最终都是完全相同的。我想我可以用一些辅助函数来分解它,比如这个:

import { EndpointBuilder } from "@reduxjs/toolkit/dist/query/endpointDefinitions";
export const makeCrudEndpoints = <T, E extends string>(
entity: E,
path: string,
build: EndpointBuilder<any, any, any>
) => {
return {
[`add${entity}`]: build.mutation<T, Partial<T>>({
query: (body) => ({ method: "POST", url: path, body }),
}),
[`list${entity}s`]: build.query<T[], {}>({
query: () => ({
method: "GET",
url: path,
}),
}),
[`get${entity}`]: build.query<T, number>({
query: (id) => ({
method: "GET",
url: `${path}/${id}`,
}),
}),
} as const;
};

并像这样使用:

const wallsApi = api.injectEndpoints({
endpoints: (build) => makeCrudEndpoints<Wall, "walls">("Wall", "walls", build),
});
export const { useGetWallQuery } = wallsApi;

但是Typescript在wallsApi:中找不到useGetWallQuery

TS2339: Property 'useGetWallQuery' does not exist on type 'Api  , { readonly [x: string]: MutationDefinition  | QueryDefinition...> | QueryDefinition...>; }, "api", never, unique symbol | unique symbol>'.

所以,是的,事情变得非常复杂,非常快,我不确定下一步该怎么做。我认为问题可能是makeCrudEndpoints函数的返回类型被推断为索引类型({[p: string]: (EndpointDefinitionWithQuery<T extends Primitive ?.....(,所以我丢失了键的类型信息。

我试着用这样的方法来解决它,但返回类型没有什么不同。

return {
[`add${entity}` as `add${E}`]: build.mutation<T, Partial<T>>({
query: (body) => ({ method: "POST", url: path, body }),
}),
...

我知道这更多地与typescript有关,而不是与rtk查询有关,但我不确定从哪里开始寻找。有什么想法吗?

我发现这篇文章提供了一个技巧,可以强制Typescript保留键类型。乍一看,以下内容似乎有效:

import { EndpointBuilder } from "@reduxjs/toolkit/dist/query/endpointDefinitions";
const makeEndpoint = <K extends string, T>(name: K, value: T) => {
return { [name]: value } as Record<K, T>;
};
export const makeCrudEndpoints =
<N extends string, T>(entityName: N, restResourcePath: string) =>
(build: EndpointBuilder<any, any, any>) => {
return {
...makeEndpoint(
`add${entityName}`,
build.mutation<T, Partial<T>>({
query: (body) => ({ method: "POST", url: restResourcePath, body }),
invalidatesTags: [`${entityName}s`],
})
),
...makeEndpoint(
`list${entityName}s`,
build.query<T[], void>({
query: () => ({
method: "GET",
url: restResourcePath,
}),
providesTags: [`${entityName}s`],
})
),
...makeEndpoint(
`get${entityName}`,
build.query<T, number>({
query: (id) => ({
method: "GET",
url: `${restResourcePath}/${id}`,
}),
})
),
} as const;
};
const wallApi = api.injectEndpoints({
endpoints: makeCrudEndpoints<"Wall", Wall>("Wall", "walls"),
});
export const { useGetWallQuery, useListWallsQuery, useAddWallMutation } =
wallApi;

这需要进一步测试,可能需要调用enhanceEndpoints()来声明标记。

我们有一些用户尝试过,但没有取得很大成功。如果你找到了解决方案,请在Github上展开讨论,以便我们在文档中了解。

一般来说,从看到您的代码并已经与一些用户进行了一些讨论中得出的一个观察结果是:

使用三个端点可能比仅使用一个端点更复杂。也许只从一个端点开始,将其提取到一个函数中,然后有三个函数,每个函数注入一个端点——您可以让一个父函数稍后单独调用这三个端点。

最新更新