While you can't do this for an arbitrary number of arguments, you can define a function that checks this for a limited number of arguments, and add more as needed:
function f<T1>(func: (a1: T1) => void, a1: T1): void;
function f<T1, T2>(func: (a1: T1, a2: T2) => void, a1: T1, a2: T2): void;
function f<T1, T2, T3>(func: (a1: T1, a2: T2, a3: T3) => void, a1: T1, a2: T2, a3: T3): void;
function f<T1, T2, T3, T4>(func: (a1: T1, a2: T2, a3: T3, a4: T4) => void, a1: T1, a2: T2, a3: T3, a4: T4): void;
// Private signature, not publicly available
function f(func: Function, ...args: any[]): void {
func.apply(null, args);
}
f(
(s: string) => { console.log(s) },
1234.56 // incorrect type
)
f(
(s: string) => { console.log(s) },
"Hello",
"World" // will accept more arguments
)
The above version will check types, but it can be invoked with more arguments. This is because the overload with 2 generic types will be chosen, and the func can have fewer arguments and still be compatible.
You can achieve full safety, if you use a 2 call approach, which locks in the func in the first call, and returns a second function that already has the number of parameters decided:
function f2<T1>(func: (a1: T1) => void): (a1: T1) => void;
function f2<T1, T2>(func: (a1: T1, a2: T2) => void): (a1: T1, a2: T2) => void;
function f2<T1, T2, T3>(func: (a1: T1, a2: T2, a3: T3) => void): (a1: T1, a2: T2, a3: T3) => void;
function f2<T1, T2, T3, T4>(func: (a1: T1, a2: T2, a3: T3, a4: T4) => void): (a1: T1, a2: T2, a3: T3, a4: T4) => void;
// Private2 signature, not publicly available
function f2(func: Function) {
return function (...args: any[]) { func.apply(null, args) };
}
f2((s: string) => { console.log(s) })(1234.56) // type mismatched
f2((s: string) => { console.log(s) })(
"Hello",
"World" // too many arguments
)
f(func: (s: string) => void, arg: string): voidwould work for what you've shown, but I get the feeling it's not that easy...