0
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[]

2
  • I tested it on TS playground and I get what you expect. Which version/compiler options of TS do you use? Commented Nov 5, 2019 at 9:41
  • I use the latest version 3.6.4. The examples are just to illustrate, what I want to achieve semantically. The problem is not the method-declaration (works fine in my code and the playground). The problem is in the body. I think I should not be required to cast the validationFunc. The issue becomes visible in playground (line 10) (see edit in question) Commented Nov 5, 2019 at 9:50

1 Answer 1

1

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

Playground Link

Sign up to request clarification or add additional context in comments.

6 Comments

okay, I added another Example which is not caught with an error if I don`t infer the inner type. sorry, I missed that earlier. If that'd not be the case, your solution works fine, but may cause an error at runtime. (with mine neither)
Edited, but needed to make some change, I don't think what you're trying to achieve is possible, if someone finds a better answer I'm curious myself.. :) I think it's ambiguous when you provide [1, 2, 3] as argument as it can be for one execution of a function with 3 params or 3 execution of a function with 1 param.
Also added a simplified version, you might just be able to do this with Array.every().
@Grabofus, Its not in my opinion. I always provide an Array. Each item in this Array will be used for one Iteration. Iff the items in this Outer Array, are themselves Arrays, they should be spread and passed as individual args to the validationFunc. [1, 2, 3] -> three iterations with 1 number each validationFunc(x) ; [[1, 2, 3]] => one call only to vaildationFunc(1,2,3).
@RobertKoritnik every is the same as a negated some, it'll stop on the first false
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.