You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.01 01:33:19