Redux Toolkit列表端点providesTags的正确TypeScript类型写法
问题描述
以下代码可正常运行,但无法为providesTags编写正确的类型定义:
type Post = { id: number, name: string, description: string, } const postsApiSlice = api.injectEndpoints({ endpoints: (builder) => ({ getPosts: builder.query<EntityState<Post>, void>({ query: ROUTES.POSTS, transformResponse: (responseData: Post[]) => { return adapter.setAll(initialState, responseData) }, // TypeScript 报错位置在 provideTags providesTags: (result) => { // result 为 undefined 时该如何处理? if (!result) return [{ type: POST_TAG, id: 'LIST' }] const tags = (result.ids.length > 0) ? // 单独返回这行类型校验可通过 result.ids.map((id) => ({ type: POST_TAG, id })) : // 单独返回这行类型校验也可通过 [{ type: POST_TAG, id: 'LIST' }] return tags } }), }), })
收到的TypeScript报错信息如下:
Type '(result: EntityState<Post> | undefined) => { type: string; id: EntityId; }[]' is not assignable to type 'ResultDescription<"Post", EntityState<Post>, void, FetchBaseQueryError, {} | undefined> | undefined'. Type '(result: EntityState<Post> | undefined) => { type: string; id: EntityId; }[]' is not assignable to type 'GetResultDescriptionFn<"Post", EntityState<Post>, void, FetchBaseQueryError, {} | undefined>'. Type '{ type: string; id: EntityId; }[]' is not assignable to type 'readonly TagDescription<"Post">[]'. Type '{ type: string; id: EntityId; }' is not assignable to type 'TagDescription<"Post">'. Type '{ type: string; id: EntityId; }' is not assignable to type 'FullTagDescription<"Post">'. Types of property 'type' are incompatible. Type 'string' is not assignable to type '"Post"'.ts(2322) endpointDefinitions.d.ts(188, 5): The expected type comes from property 'providesTags' which is declared here on type 'Omit<EndpointDefinitionWithQuery<void, BaseQueryFn<string | FetchArgs, unknown, FetchBaseQueryError, {}, {}>, EntityState<Post>> & { ...; } & { ...; } & QueryExtraOptions<...>, "type"> | Omit<...>'
如果单独返回result.ids.map((id) => ({ type: POST_TAG, id })),或是单独返回[{ type: POST_TAG, id: 'LIST' }],类型校验均可正常通过。
存在两个疑问:
- 该如何正确编写此处的类型定义?
- 当result为
undefined时应当如何处理?是否返回[{ type: POST_TAG, id: 'LIST' }]即可?
解决方案
报错根因
报错核心是TypeScript对三元表达式返回值的类型推断出现宽化:两个分支单独返回时TS能识别到POST_TAG是字面量类型"Post",但合并到三元表达式返回时,id的类型同时存在EntityId(number/string)和字符串字面量'LIST',导致整个对象的type字段被宽化为通用string类型,和RTK Query要求的tag type必须是和tagTypes定义完全匹配的字面量类型规则冲突。
修复写法
方案1:用as const固化类型(官方推荐)
首先确保POST_TAG用const声明为字面量常量,给返回的标签数组加as const断言阻止类型宽化即可,不需要额外写类型标注:
// 先确保POST_TAG是const声明的常量 const POST_TAG = "Post"; providesTags: (result) => { // 兜底返回LIST标签 if (!result) return [{ type: POST_TAG, id: 'LIST' }] as const; // 可以直接合并返回LIST和单条标签,不需要判断ids长度,空数组展开无副作用 return [ { type: POST_TAG, id: 'LIST' }, ...result.ids.map(id => ({ type: POST_TAG, id })) ] as const; }
方案2:显式声明返回类型
如果不想用as const,可以显式标注返回值为RTK Query内置的TagDescription类型:
import type { TagDescription } from '@reduxjs/toolkit/query'; providesTags: (result): TagDescription<typeof POST_TAG>[] => { if (!result) return [{ type: POST_TAG, id: 'LIST' }]; return result.ids.length ? result.ids.map(id => ({ type: POST_TAG, id })) : [{ type: POST_TAG, id: 'LIST' }]; }
result为undefined的处理逻辑
当请求失败、或者请求还未完成时result就会是undefined,此时返回[{ type: POST_TAG, id: 'LIST' }]是完全符合RTK Query约定的标准处理方式:
- 这个标签会在请求失败时也保留缓存关联,不会导致后续
invalidateTags失效 - 不会引发额外的类型或运行时错误
内容的提问来源于stack exchange,提问作者Rashomon
相关产品推荐
相关产品推荐

