Is it possible for a function with a parameter of a conditional type to return conditionally based on that parameter?
Explore the concept further here
I am faced with a scenario where I have a function that takes one parameter, which can either be a custom type (QueryKey
) or a function:
export function createHandler(
createQueryKeyOrQueryKey: QueryKey | ((idForQueryKey: string) => QueryKey),
) { ... }
Depending on this parameter, the createHandler
function needs to return different types:
return {
createState:
typeof createQueryKeyOrQueryKey !== "function"
? (data) => createState(data, createQueryKeyOrQueryKey)
: (data, id) => createState(data, createQueryKeyOrQueryKey(id)),
};
The type of what is returned from createState
is also subject to conditions:
createState:
| ((data: TData) => State)
| ((data: TData, idForQueryKey: string) => State);
When using createHandler
to create a handler, there are two ways it can be utilized:
handler.createState({ a: 123 })
handler.createState({ a: 123 }, "some-id")
However, only one form of createState
should be permissible at a time. The choice between the two should depend on how the handler is created, whether a query key OR a function is provided:
// Option 1:
//
// Using query key directly
const queryKey = "some-query-key"
const handler1 = createHandler(queryKey)
// ✅ Allowed
handler1.createState({ a: 123 })
// ❌ Not allowed
handler1.createState({ a: 123 }, "some-id")
// Option 2:
//
// Using query key as a function for creation
const queryKeyCreator = (id: string) => "some-query-key" + id
const handler2 = createHandler(queryKeyCreator)
// ❌ Not allowed
handler2.createState({ a: 123 })
// ✅ Allowed
handler2.createState({ a: 123 }, "some-id")
At present, the return type does not work correctly, resulting in data
being of type any
:
Why is this happening? TypeScript recognizes that createState
has conditional behavior and the variant with just one parameter (data
) should also be a valid option based on the type of createState
.
By the way, is there a better solution to tackle this issue? Perhaps utilizing function overloading or discriminating unions via keys could be feasible options, although implementing them based on the decision of the caller regarding which variant (key or function) to use poses some uncertainty.