简体   繁体   中英

Typescript: How to narrow down function return type based on function parameter value

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 (EG: getPosts(), updatePost() etc).

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 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

Playground link to code

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM