Reputation: 1729
I have an API client that can be sync or async, depending on the fetcher. Dramatically simplified, it's like this:
function makeClient<F>({fetcher}: any): any {
return {
query: () => fetcher(`query`) as F<QueryResult>,
toc: () => fetcher(`toc`) as F<TocResult>,
}
}
makeClient<Promise>({fetcher: window.fetch}) // returns promises
makeClient<Identity>({fetcher: fetchSync}) // returns data
I'm running into Type 'F' is not generic.
Is there a way to do this?
Here is the playground: https://tsplay.dev/N54OMw
Upvotes: 3
Views: 3075
Reputation: 1421
Type parameters stand in for composed types, so as far as I know they can't be generic in the way it looks like you want.
You can, however, make your return type depend on a type argument using conditional types. Here's a very explicit way to do it:
interface QueryResult {}
interface TocResult {}
const fetchSync = () => ({})
// type alias we'll use to signal whether or not to wrap the result in a promise
type PromiseIndicator = 'promise_indicator'
// conditional type that wraps the second argument in a promise if, and only if, the first type argument is set to PromiseIndicator
type ConditionalPromise<T, F> = T extends PromiseIndicator ? Promise<F> : F
// F = string is just a default; it could be anything other than PromiseIndicator
function makeClient<F = string>({fetcher}: any) {
return {
query: () => fetcher(`query`) as ConditionalPromise<F,QueryResult>,
toc: () => fetcher(`toc`) as ConditionalPromise<F,TocResult>,
}
}
makeClient(fetchSync)
makeClient<PromiseIndicator>(fetchSync)
I suspect there's a cleverer way to do this in your actual code based on the actual type of fetcher
, but for the snippet you've provided this should work.
Upvotes: 1