Reputation: 1833
function validate<K>(validationFunc: (...args: (K extends Array<infer T> ? T : K)[]) => boolean, validationArgs: K[]): boolean {
let res: boolean;
for (const validationArg of validationArgs) {
if (Array.isArray(validationArg)) {
res = validationFunc(...validationArg);
} else {
// res = validationFunc(validationArg);
res = (validationFunc as (args: K) => boolean)(validationArg);
}
if(!res)
return false;
}
return true
}
The commented line throws an Error at the argument: the Argument of type 'K' is not assignable to parameter of type 'K extends (infer T)[] ? T : K'.ts(2345)
, whereas the casted version works, and does not throw any errors.
As seen in this playground.
Why is typescript not able to infer, that on this line, K
cannot be of type Array<any>
and thus is allowed to be passed to the validation function?
Semantically:
If the second argument is of type K[]
, the function needs to accept K
as a single parameter.
If the second argument is of type K[][]
, the function needs to accept multiple arguments of K
.
E.g.
validate((x: number) => x%2, [1, 2, 3])
should be ok
validate((a: string, b: string) => a === b, [['a', 'a'], ['b', 'b']])
should be ok
validate((x: number) => x%2, ['a', 'b'])
should throw an error
validate((x: number) => x%2, [['a', 'a'], ['b', 'b']])
should throw an error
EDIT:
validate((x: number) => x % 2 === 0, [[1, 2, 3]])
should also throw an error, since validate would destructure the number[][]
once, and try to call (x: number) => boolean
with number[]
Upvotes: 0
Views: 475
Reputation: 2034
I don't think you need to infer the type of the param function. Your validation arguments can be just K[] | K[][]
as you explained it.
I also did a small change when you call the validate function x % 2
should be wrapped in Boolean()
otherwise the return value would be incorrect.
function validate<T extends (...args: any) => boolean, P extends Parameters<T>>(validationFunc: T, validationArgs: P[]): boolean {
let res: boolean;
for (const validationArg of validationArgs) {
res = validationFunc(validationArg);
if(!res)
return false;
}
return true
}
function simplified<T extends (...args: any) => boolean, P extends Parameters<T>>(validationFunc: T, validationArgs: P[]): boolean {
return validationArgs.every((args) => validationFunc(args));
}
validate((x: number) => Boolean(x % 2), [[1], [2], [3]]) // should be ok
validate((a: string, b: string) => a === b, [['a', 'a'], ['b', 'b']]) // should be ok
validate((x: number) => Boolean(x % 2), [['a'], ['b']]) // should throw an error
validate((x: number) => Boolean(x % 2), [['a', 'a'], ['b', 'b']]) // should throw an error
validate((x: number) => x % 2 === 0, [[1, 2, 3]]); // should throw an error
Upvotes: 1