How to infer return type of Promise based on function arguments?

Sam Bautista :
type Actions =
  | ['add', number, number] // should return number
  | ['log', string]; // should return void


type Call = (...args: Actions) => Promise<?>;

const call: Call = (...args: Actions): Promise<?> => {
  // returns some Promise
}

call('add', 1, 1).then(value => {
   // value is then inferred as number
})


call('log', 'Hello World!').then(value => {
   // value is then inferred as void
})

How do you base the return value of the Promise from whatever arguments was passed to the function?

T.J. Crowder :

Two approaches for you:

  1. With your Call type as an overloaded function type
  2. With just an overloaded function.

With your Call type

The type you'd want for Call is an overloaded function type. You can define it like this:

type Call = {
    (...args: ['add', number, number]): Promise<number>;
    (...args: ['log', string]): Promise<void>;
};

Since you need to associate a return type with the parameter list, the Actions type doesn't really help.

A function typed with that type will do the inference you've asked for:

function doSomething(fn: Call) {
    fn('add', 1, 2)
        .then(value => {
            // Here, TypeScript infers `value` is of type `number`
        });
    fn('log', 'message')
        .then(value => {
            // Here, TypeScript infers `avlue` is of type `void`
        });
}

On the playground

If you're going to write functions for that, it may help to have some helper types:

type AddParams = ['add', number, number];
type LogParams = ['log', string];
type ActionParams =
    | AddParams
    | LogParams;

type Call = {
    (...args: AddParams): Promise<number>;
    (...args: LogParams): Promise<void>;
};

Then for instance:

const call: Call = (...args: ActionParams): Promise<any> => {
    // (Dummy implementation)
    if (args[0] === 'add') {
        return Promise.resolve(args[1] + args[2]);
    }
    return Promise.resolve();
};

On the playground

With just an overloaded function

If you just want to write an overloaded function, you don't need the Call type (you probably know that):

type AddAction = ['add', number, number];
type LogAction = ['log', string];
type Actions =
    | AddAction
    | LogAction;

function call(...args: AddAction): Promise<number>;
function call(...args: LogAction): Promise<void>;
function call(...args: Actions): Promise<any> {
    // ...implementation...
}

call('add', 1, 2)
    .then(value => {
        // Here, TypeScript infers `value` is of type `number`
    });
call('log', 'message')
    .then(value => {
        // Here, TypeScript infers `avlue` is of type `void`
    });

On the playground

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=11440&siteId=1