I have following code (simplified):
const todoApi = {
add(name: string) {},
edit(id: number, name: string) {},
}
type TodoApi = typeof todoApi;
type TodoCommandName = keyof TodoApi
function createOptimisticUpdate<TCommand extends TodoApi[TodoCommandName]>(
command: TCommand,
localUpdate: (...p: Parameters<TCommand>) => void
) {
return (...p: Parameters<TCommand>) => {
localUpdate(...p);
command(...p) // should work but causes Type Error: A spread argument must either have a tuple type or be passed to a rest parameter.(2556)
command(1) // not expected to work but the error reveals expected params: (parameter) command: (arg0: never, name: string) => void, Expected 2 arguments, but got 1.(2554)
command(...p as [never, string]) // workaround :(
};
}
// from outside, when used with one command, everything works as expected
createOptimisticUpdate(todoApi.add, name => {})("name")
createOptimisticUpdate(todoApi.edit, (id, newName) => {})(1, "newName")
createOptimisticUpdate(todoApi.edit, (name) => {/* we have declared only one parameter with wrong name, but type is correctly inferred as number */})("newName") // error: Expected 2 arguments, but got 1.
// but when we try to use more commands at once (not wanted), things are getting strange
let addOrEdit = Math.random() ? todoApi.add : todoApi.edit;
createOptimisticUpdate(addOrEdit, () =>{})("a") // not expected: both signatures are valid
/*
type: function createOptimisticUpdate<((name: string) => void) | ((id: number, name: string) => void)>(command: ((name: string) => void) | ((id: number, name: string) => void), localUpdate: (...p: [name: string] | [id: number, name: string]) => void): (...p: [name: string] | [id: number, name: string]) => void
*/
When used with one command, everything works as expected, but the problem seems to be the usage is not restricted to that. Is there any way to force this?