lukemorales / query-key-factory

A library for creating typesafe standardized query keys, useful for cache management in @tanstack/query
https://www.npmjs.com/package/@lukemorales/query-key-factory
MIT License
1.16k stars 32 forks source link

Is it possible to pass generic value to query? #91

Closed william-hotmart closed 2 months ago

william-hotmart commented 2 months ago

I have a query structured like this:

import { createQueryKeys } from '@lukemorales/query-key-factory'

...

const myQueries = createQueryKeys('my', {
  byId(id: string) {
    return {
      queryKey: [id],
      queryFn: () => Service.getById(queryId)
    }
  }
})

The getById method in the Service makes a request to an endpoint that can return different responses. To handle this, I'm using a generic.

async getById<T>(queryId: string) {
    const { data } = await api.get<T>(ServiceEndpoints.byId(queryId))

    return data
}

However, if I use a generic in the byId function and pass it down to getById, it does not work, and the response value remains as unknown.

// Does not work

const myQueries = createQueryKeys('my', {
  byId<T>(id: string) {
    return {
      queryKey: [id],
      queryFn: () => Service.getById<T>(queryId)
    }
  }
})

function useById<T>(queryId: string) {
  return useQuery(myQueries.byId<T>(queryId))
}

Am I doing something wrong, or is it really impossible to pass down a generic using createQueryKeys?

linear[bot] commented 2 months ago

OSS-12 Is it possible to pass generic value to query?

lukemorales commented 2 months ago

@william-hotmart It is not possible (AFAIK it's a Typescript limitation). Your service might serve well with abstracted generics, but too much abstractions are dangerous. I'd suggest creating different hooks and keys for different entities, even if they use the same endpoint:

const users = createQueryKeys('users', {
  byId(id: string) {
    return {
      queryKey: [id],
      queryFn: () => Service.getById<User>(queryId)
    }
  }
})

const todos = createQueryKeys('todos', {
  byId(id: string) {
    return {
      queryKey: [id],
      queryFn: () => Service.getById<Todo>(queryId)
    }
  }
})

function useUser(id: string) {
  return useQuery(users.byId(id))
}

function useTodo(id: string) {
  return useQuery(todos.byId(id))
}
william-hotmart commented 2 months ago

You're right @lukemorales. Makes more sense to separate those responsibilities. Thanks!