lukemorales/query-key-factory

Is it possible to pass generic value to query?

Closed this issue · 3 comments

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?

@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))
}

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