Is it possible to spread the arguments of a function into an array of generic types?
I'm trying to add a TypeScript type to a generic function that takes a function and returns a function. The passed function's parameters relate to the returned function's parameters. I'd like the consumer to be able to add types to the passed in function and have those types appear on the returned function.
I've got a basic solution to the problem, but it only works with a fixed number of parameters. Is it possible to spread generic parameters in the below way?
Example usage:
type Foo = // Generic solution here
const bar = foo((state, name: string, age: number));
bar('John', 22); // All good
bar('John', 'Doe'); // Type Error, 'Doe' is not a number
const baz = foo((state, firstName: string, lastName: string, age: number));
baz('John', 'Doe', 22)
This is for a React Hook integration. There are some really good examples where providers have done a great job to make TypeScript type safety really easy for their consumers.
A great example is TypedUseSelectorHook from React Redux.
https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/react-redux/index.d.ts#L556
Attempt 1 A solution with spreading. But unfortunately all types are a union of all possible types.
export type foo<State> = <Args>(
    baz: (state: State, ...args: Args[]) => Partial<State>
) => (...args: Args[]) => any;
Attempt 2 A solution with a 'fixed' number of parameters. This works, but the autocomplete shows all parameters. And if the consumer tries to use more than the number of types added TypeScript will be unhappy.
type foo<State> = <
    Arg0 = void,
    Arg1 = void,
    Arg2 = void,
    Arg3 = void,
    Arg4 = void,
    Arg5 = void
>(
    baz: (
        state: State,
        arg0: Arg0,
        arg1: Arg1,
        arg2: Arg2,
        arg3: Arg3,
        arg4: Arg4,
        arg5: Arg5
    ) => Partial<State>
) => (
    arg0: Arg0,
    arg1: Arg1,
    arg2: Arg2,
    arg3: Arg3,
    arg4: Arg4,
    arg5: Arg5
) => any;
What I'd love to get working is something like:
type Foo<State> = <PassedArgs>(
    baz: (
        state: State,
        ...args: PassedArgs // Error: A rest parameter must be of an array type.
    ) => Partial<State>
) => (args: PassedArgs) => any;
This would allow the consumer to pass in a function that takes any number of parameters and parameter types and the return function would be correctly typed.
Your solution is almost correct, you just need to tell TS that Args is constrained to being an array-like thing (tuple of arguments in this case).
export type Foo<State> = <Args extends any[]>(
    baz: (state: State, ...args: Args) => Partial<State>
) => (...args: Args) => any;
                        If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With