Reputation: 103
I'm fairly new to typescript and can't figure out a way to properly narrow down the return type of a function based on an argument of that same function.
Consider the following code:
import exampleApiResource from './resources/example';
import exampleApiResource2 from './resources/example2';
import { ApiResources } from './typings';
const apiResources: ApiResources = {
exampleResource: exampleApiResource,
exampleResource2: exampleApiResource2,
};
export function useApiClient(resource: keyof ApiResources) {
return apiResources[resource];
}
export default apiResources;
/** typings **/
export type ApiResources = {
exampleResource: ExampleType;
exampleResource2: ExampleType2;
};
export type ExampleType = {
getExample: () => Promise<TestResource>;
};
export type ExampleType2 = {
getExample2: () => Promise<TestResource>;
};
I want to expose the apiResources object through the useApiClient function. The exampleApiResource and exampleApiResource2 hold different properties, as you can see in the typings.
Typescript infers the useApiClient function to return the following types: ExampleType | ExampleType2
How would one narrow this down to a specific type, is this even possible?
--
Use case:
Expose an axios based API client through a function. The function accepts a parameter which will resolve to an object which contains functions to perform API related actions (E.G: getPosts(), updatePost() etc).
Upvotes: 2
Views: 1214
Reputation: 330571
You should make useApiClient()
a generic function whose type parameter K
corresponds to the particular member(s) of keyof ApiResources
passed in as resource
:
export function useApiClient<K extends keyof ApiResources>(resource: K) {
return apiResources[resource];
}
Now the return type of the function is ApiResources[K]
, an indexed access type corresponding to the type of the property value of type ApiResources
at a key of type K
.
You can verify that this will behave as desired when you call useApiClient()
and return either ExampleType
or ExampleType2
depending on the input:
const ex = useApiClient("exampleResource"); // const ex: ExampleType
const ex2 = useApiClient("exampleResource2"); // const ex2: ExampleType2
You'll only get the union ExampleType | ExampleType2
in cases where the input type is also a union:
const exUnion = useApiClient(
Math.random() < 0.5 ? "exampleResource" : "exampleResource2"
);
// const exUnion: ExampleType | ExampleType2
Upvotes: 1