reduxjs/redux

providesTags does not work with callback when defining return type via TypeScript generic

shennan opened this issue · 4 comments

Prior Issues

Can't seem to find any

What is the current behavior?

With RTK Query, if using a providesTags callback on a simple query while dictating return types/arguments with a generic, you receive a complicated TypeScript error:

Type '(result: PostId[] | undefined, error: FetchBaseQueryError | undefined, arg: void) => ("Post" | { type: string; id: string; })[]' is not assignable to type 'ResultDescription<"Post", PostId[], void, FetchBaseQueryError, FetchBaseQueryMeta | undefined> | undefined'.
  Type '(result: PostId[] | undefined, error: FetchBaseQueryError | undefined, arg: void) => ("Post" | { type: string; id: string; })[]' is not assignable to type 'GetResultDescriptionFn<"Post", PostId[], void, FetchBaseQueryError, FetchBaseQueryMeta | undefined>'.
    Type '("Post" | { type: string; id: string; })[]' is not assignable to type 'readonly TagDescription<"Post">[]'.
      Type '"Post" | { type: string; id: string; }' is not assignable to type 'TagDescription<"Post">'.
        Type '{ type: string; id: string; }' is not assignable to type 'TagDescription<"Post">'.
          Type '{ type: string; id: string; }' 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(223, 5): The expected type comes from property 'providesTags' which is declared here on type 'OmitFromUnion<QueryDefinition<void, BaseQueryFn<string | FetchArgs, unknown, FetchBaseQueryError, {}, FetchBaseQueryMeta>, "Post", PostId[], "api">, "type">'

Steps to Reproduce

Try and define return type and params as generic for builder.query<any, any> and TypeScript complains about your advanced providesTags usage:

type Post = {
  id: string
  title: string
  content: string
}

export const apiSlice = createApi({
  reducerPath: 'api',
  baseQuery: fetchBaseQuery({ baseUrl: '/fakeApi' }),
  tagTypes: ['Post'],
  endpoints: builder => ({
    getPosts: builder.query<Post[], void>({
      query: () => '/posts',
      providesTags: (result = [], error, arg) => [
        'Post',
        ...result.map(({ id }) => ({ type: 'Post', id }))
      ]
    }),
  })
})

The above example is practically identical to the tutorial describing advanced RTK Query patterns (have shaved some endpoints to focus on the issue) and differs only that in my second code example I'm attempting to use a type and a generic to dictate the return type and arguments for the GET request.

Note

If I @ts-ignore, things seem to work as expected.

What is the expected behavior?

There should be no TypeScript errors.

Environment Details

Version of Redux is v8.1.3. Not sure if this worked before.

try adding as const to the type, i.e. result.map(({ id }) => ({ type: 'Post' as const, id }))

@EskiMojo14 Thanks. This appears to work, but can you explain why?

Sure - basically inside your map callback Typescript isn't aware that type can only be a specific set of strings, it just infers that it's a string. Adding as const means it becomes the literal type 'Post' which matches the type requirements for tags.

Thanks for the explanation.