'Is it possible to decide the return type of a function at runtime, based on a function argument?

This is best explained with an example!

interface Query {
  foo: any;
}

interface Mutation {
  bar: any;
}

type OperationName = keyof Query | keyof Mutation;

const request = async <T>(args, operationName: OperationName): Promise<{ data: { [operationName]: T } }> => {

};

Ideally what I want to happen is if I call request like this:

const result = await request<string>({}, 'foo');

then result looks like this:

{
  data: {
    foo: string
  }
}

However this gives the error A computed property name in a type literal must refer to an expression whose type is a literal type or a 'unique symbol' type. ts(1170)

I feel like this should be possible as the type OperationName is known at compile time so there should be some way for the compiler to figure out all the possible return types of request in advance. I've no idea how to do it though!



Solution 1:[1]

This answer only discusses how to type the function properly, not do the conversion at runtime.

You must implement yourself the conversion somehow (based on operation, arguments passed, etc).

You can use a mapped type here:

const request = async <T, O extends OperationName>(args: unknown, operationName: O): Promise<{ data: { [K in O]: T } }> => {

We need another generic parameter O which is an OperationName to "retain" what was passed to operationName. Then we use a mapped type which maps O to T.

But we aren't done quite yet. Notice that when we call it:

request<string>(???, "foo")

We get an error because we didn't provide a parameter for O. We can pass "foo" again but that is redundant, so we'll work around it by currying:

const request = <T>() => async <O extends OperationName>(args: unknown, operationName: O): Promise<{ data: { [K in O]: T } }> => {

Now we can pass T separate of O:

request<string>()(???, "foo")

Playground with example calls

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 hittingonme