A benefit of a strong type system is the ability to have good guard rails that tell you when you’ve writing an invalid program, without having to run it. However, to be truly useful it needs to be possible and ideally easy to define interfaces and method signatures once, and simply transform these for their various applications.

In react/redux applications you define actions, and these are dispatched to mutate state or trigger side effects. I’m just using this as a way of introducing the TypeScript example, rather than suggesting what follows is a good way of solving this problem (e.g., libraries like Typesafe Actions exists to solve this problem specifically).

We define:

  1. An action object (AO), which is typically a discriminator union, that encapsulates the information about the action
  2. An action creator, which is a function that creates the action object. (...args) => AO
  3. Then we connect our components to redux, which provides bound calls using the arguments of the action creator but which actually returns void

Lets assume defining an interface for the action object and the action creator function are uncontroversial.

interface AO {
    type: 'action',
    payload: {
        name: string
    }
}

function performAction(name: string): AO {
    return {
        type: 'action',
        payload: {
            name
        }
    };
}

How do we easily type the dispatch version of the performAction function such that we can reuse the function signature that we’ve already defined?

My first pass was to use the typeof keywords.

interface DispatchProps {
    performAction: typeof performAction;
}

The problem here is that it also gets the return type of AO, which, while not being a show stopper, doesn’t correctly represent the function.

Upon reading this summary of typescript utility types it finally became clear how to reuse the arguments without bringing in the whole function definition.

type VoidReturn<F extends (...args: any) => any> = (...params: Parameters<F>) => void;

interface DispatchProps {
    performAction: VoidReturn<typeof performAction>;
}